All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
@ 2023-01-17 18:00 ` Vadim Fedorenko
  0 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, netdev, linux-arm-kernel, linux-clk

Implement common API for clock/DPLL configuration and status reporting.
The API utilises netlink interface as transport for commands and event
notifications. This API aim to extend current pin configuration and
make it flexible and easy to cover special configurations.

v4 -> v5:
 * fix code issues found during last reviews:
   - replace cookie with clock id
	 - follow one naming schema in dpll subsys
	 - move function comments to dpll_core.c, fix exports
	 - remove single-use helper functions
	 - merge device register with alloc
   - lock and unlock mutex on dpll device release
   - move dpll_type to uapi header
   - rename DPLLA_DUMP_FILTER to DPLLA_FILTER
   - rename dpll_pin_state to dpll_pin_mode
   - rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
   - remove DPLL_CHANGE_PIN_TYPE enum value
 * rewrite framework once again (Arkadiusz)
   - add clock class:
     Provide userspace with clock class value of DPLL with dpll device dump
     netlink request. Clock class is assigned by driver allocating a dpll
     device. Clock class values are defined as specified in:
     ITU-T G.8273.2/Y.1368.2 recommendation.
   - dpll device naming schema use new pattern:
	   "dpll_%s_%d_%d", where:
       - %s - dev_name(parent) of parent device,
       - %d (1) - enum value of dpll type,
       - %d (2) - device index provided by parent device.
   - new muxed/shared pin registration:
	   Let the kernel module to register a shared or muxed pin without finding
     it or its parent. Instead use a parent/shared pin description to find
     correct pin internally in dpll_core, simplifing a dpll API
 * Implement complex DPLL design in ice driver (Arkadiusz)
 * Remove ptp_ocp driver from the series for now
v3 -> v4:
 * redesign framework to make pins dynamically allocated (Arkadiusz)
 * implement shared pins (Arkadiusz)
v2 -> v3:
 * implement source select mode (Arkadiusz)
 * add documentation
 * implementation improvements (Jakub)
v1 -> v2:
 * implement returning supported input/output types
 * ptp_ocp: follow suggestions from Jonathan
 * add linux-clk mailing list
v0 -> v1:
 * fix code style and errors
 * add linux-arm mailing list

Arkadiusz Kubalewski (2):
  ice: add admin commands to access cgu configuration
  ice: implement dpll interface to control cgu

Vadim Fedorenko (2):
  dpll: documentation on DPLL subsystem interface
  dpll: Add DPLL framework base functions

 Documentation/networking/dpll.rst             |  280 +++
 Documentation/networking/index.rst            |    1 +
 MAINTAINERS                                   |    8 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/dpll/Kconfig                          |    7 +
 drivers/dpll/Makefile                         |    9 +
 drivers/dpll/dpll_core.c                      | 1010 ++++++++
 drivers/dpll/dpll_core.h                      |  105 +
 drivers/dpll/dpll_netlink.c                   |  883 +++++++
 drivers/dpll/dpll_netlink.h                   |   24 +
 drivers/net/ethernet/intel/Kconfig            |    1 +
 drivers/net/ethernet/intel/ice/Makefile       |    3 +-
 drivers/net/ethernet/intel/ice/ice.h          |    5 +
 .../net/ethernet/intel/ice/ice_adminq_cmd.h   |  240 +-
 drivers/net/ethernet/intel/ice/ice_common.c   |  467 ++++
 drivers/net/ethernet/intel/ice/ice_common.h   |   43 +
 drivers/net/ethernet/intel/ice/ice_dpll.c     | 2115 +++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h     |   99 +
 drivers/net/ethernet/intel/ice/ice_lib.c      |   17 +-
 drivers/net/ethernet/intel/ice/ice_main.c     |   10 +
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c   |  408 ++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 ++
 drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
 include/linux/dpll.h                          |  282 +++
 include/uapi/linux/dpll.h                     |  294 +++
 26 files changed, 6549 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/networking/dpll.rst
 create mode 100644 drivers/dpll/Kconfig
 create mode 100644 drivers/dpll/Makefile
 create mode 100644 drivers/dpll/dpll_core.c
 create mode 100644 drivers/dpll/dpll_core.h
 create mode 100644 drivers/dpll/dpll_netlink.c
 create mode 100644 drivers/dpll/dpll_netlink.h
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
 create mode 100644 include/linux/dpll.h
 create mode 100644 include/uapi/linux/dpll.h

-- 
2.30.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
@ 2023-01-17 18:00 ` Vadim Fedorenko
  0 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, netdev, linux-arm-kernel, linux-clk

Implement common API for clock/DPLL configuration and status reporting.
The API utilises netlink interface as transport for commands and event
notifications. This API aim to extend current pin configuration and
make it flexible and easy to cover special configurations.

v4 -> v5:
 * fix code issues found during last reviews:
   - replace cookie with clock id
	 - follow one naming schema in dpll subsys
	 - move function comments to dpll_core.c, fix exports
	 - remove single-use helper functions
	 - merge device register with alloc
   - lock and unlock mutex on dpll device release
   - move dpll_type to uapi header
   - rename DPLLA_DUMP_FILTER to DPLLA_FILTER
   - rename dpll_pin_state to dpll_pin_mode
   - rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
   - remove DPLL_CHANGE_PIN_TYPE enum value
 * rewrite framework once again (Arkadiusz)
   - add clock class:
     Provide userspace with clock class value of DPLL with dpll device dump
     netlink request. Clock class is assigned by driver allocating a dpll
     device. Clock class values are defined as specified in:
     ITU-T G.8273.2/Y.1368.2 recommendation.
   - dpll device naming schema use new pattern:
	   "dpll_%s_%d_%d", where:
       - %s - dev_name(parent) of parent device,
       - %d (1) - enum value of dpll type,
       - %d (2) - device index provided by parent device.
   - new muxed/shared pin registration:
	   Let the kernel module to register a shared or muxed pin without finding
     it or its parent. Instead use a parent/shared pin description to find
     correct pin internally in dpll_core, simplifing a dpll API
 * Implement complex DPLL design in ice driver (Arkadiusz)
 * Remove ptp_ocp driver from the series for now
v3 -> v4:
 * redesign framework to make pins dynamically allocated (Arkadiusz)
 * implement shared pins (Arkadiusz)
v2 -> v3:
 * implement source select mode (Arkadiusz)
 * add documentation
 * implementation improvements (Jakub)
v1 -> v2:
 * implement returning supported input/output types
 * ptp_ocp: follow suggestions from Jonathan
 * add linux-clk mailing list
v0 -> v1:
 * fix code style and errors
 * add linux-arm mailing list

Arkadiusz Kubalewski (2):
  ice: add admin commands to access cgu configuration
  ice: implement dpll interface to control cgu

Vadim Fedorenko (2):
  dpll: documentation on DPLL subsystem interface
  dpll: Add DPLL framework base functions

 Documentation/networking/dpll.rst             |  280 +++
 Documentation/networking/index.rst            |    1 +
 MAINTAINERS                                   |    8 +
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    1 +
 drivers/dpll/Kconfig                          |    7 +
 drivers/dpll/Makefile                         |    9 +
 drivers/dpll/dpll_core.c                      | 1010 ++++++++
 drivers/dpll/dpll_core.h                      |  105 +
 drivers/dpll/dpll_netlink.c                   |  883 +++++++
 drivers/dpll/dpll_netlink.h                   |   24 +
 drivers/net/ethernet/intel/Kconfig            |    1 +
 drivers/net/ethernet/intel/ice/Makefile       |    3 +-
 drivers/net/ethernet/intel/ice/ice.h          |    5 +
 .../net/ethernet/intel/ice/ice_adminq_cmd.h   |  240 +-
 drivers/net/ethernet/intel/ice/ice_common.c   |  467 ++++
 drivers/net/ethernet/intel/ice/ice_common.h   |   43 +
 drivers/net/ethernet/intel/ice/ice_dpll.c     | 2115 +++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h     |   99 +
 drivers/net/ethernet/intel/ice/ice_lib.c      |   17 +-
 drivers/net/ethernet/intel/ice/ice_main.c     |   10 +
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c   |  408 ++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 ++
 drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
 include/linux/dpll.h                          |  282 +++
 include/uapi/linux/dpll.h                     |  294 +++
 26 files changed, 6549 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/networking/dpll.rst
 create mode 100644 drivers/dpll/Kconfig
 create mode 100644 drivers/dpll/Makefile
 create mode 100644 drivers/dpll/dpll_core.c
 create mode 100644 drivers/dpll/dpll_core.h
 create mode 100644 drivers/dpll/dpll_netlink.c
 create mode 100644 drivers/dpll/dpll_netlink.h
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
 create mode 100644 include/linux/dpll.h
 create mode 100644 include/uapi/linux/dpll.h

-- 
2.30.2


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

* [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-01-17 18:00 ` Vadim Fedorenko
@ 2023-01-17 18:00   ` Vadim Fedorenko
  -1 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, netdev, linux-arm-kernel, linux-clk,
	Milena Olech, Michal Michalik

DPLL framework is used to represent and configure DPLL devices
in systems. Each device that has DPLL and can configure sources
and outputs can use this framework. Netlink interface is used to
provide configuration data and to receive notification messages
about changes in the configuration or status of DPLL device.
Inputs and outputs of the DPLL device are represented as special
objects which could be dynamically added to and removed from DPLL
device.

Co-developed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Co-developed-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
---
 MAINTAINERS                 |    8 +
 drivers/Kconfig             |    2 +
 drivers/Makefile            |    1 +
 drivers/dpll/Kconfig        |    7 +
 drivers/dpll/Makefile       |    9 +
 drivers/dpll/dpll_core.c    | 1010 +++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h    |  105 ++++
 drivers/dpll/dpll_netlink.c |  883 ++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |   24 +
 include/linux/dpll.h        |  282 ++++++++++
 include/uapi/linux/dpll.h   |  294 ++++++++++
 11 files changed, 2625 insertions(+)
 create mode 100644 drivers/dpll/Kconfig
 create mode 100644 drivers/dpll/Makefile
 create mode 100644 drivers/dpll/dpll_core.c
 create mode 100644 drivers/dpll/dpll_core.h
 create mode 100644 drivers/dpll/dpll_netlink.c
 create mode 100644 drivers/dpll/dpll_netlink.h
 create mode 100644 include/linux/dpll.h
 create mode 100644 include/uapi/linux/dpll.h

diff --git a/MAINTAINERS b/MAINTAINERS
index f82dd8d43c2b..de8a10b21ce8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6411,6 +6411,14 @@ F:	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
 F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
 F:	drivers/net/ethernet/freescale/dpaa2/dpsw*
 
+DPLL CLOCK SUBSYSTEM
+M:	Vadim Fedorenko <vadfed@fb.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/dpll/*
+F:	include/net/dpll.h
+F:	include/uapi/linux/dpll.h
+
 DRBD DRIVER
 M:	Philipp Reisner <philipp.reisner@linbit.com>
 M:	Lars Ellenberg <lars.ellenberg@linbit.com>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 968bd0a6fd78..453df9e1210d 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
 
 source "drivers/hte/Kconfig"
 
+source "drivers/dpll/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index bdf1c66141c9..7cbee58bc692 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER)		+= counter/
 obj-$(CONFIG_MOST)		+= most/
 obj-$(CONFIG_PECI)		+= peci/
 obj-$(CONFIG_HTE)		+= hte/
+obj-$(CONFIG_DPLL)		+= dpll/
diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
new file mode 100644
index 000000000000..a4cae73f20d3
--- /dev/null
+++ b/drivers/dpll/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Generic DPLL drivers configuration
+#
+
+config DPLL
+  bool
diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
new file mode 100644
index 000000000000..b18cf848a010
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DPLL drivers.
+#
+
+obj-$(CONFIG_DPLL)          += dpll_sys.o
+dpll_sys-y                  += dpll_core.o
+dpll_sys-y                  += dpll_netlink.o
+
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..fec534f17827
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,1010 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  dpll_core.c - Generic DPLL Management class support.
+ *
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dpll_core.h"
+
+/**
+ * struct dpll_pin - structure for a dpll pin
+ * @idx:		unique id number for each pin
+ * @parent_pin:		parent pin
+ * @type:		type of the pin
+ * @ops:		operations this &dpll_pin supports
+ * @priv:		pointer to private information of owner
+ * @ref_dplls:		array of registered dplls
+ * @description:	name to distinguish the pin
+ */
+struct dpll_pin {
+	u32 idx;
+	struct dpll_pin *parent_pin;
+	enum dpll_pin_type type;
+	struct dpll_pin_ops *ops;
+	void *priv;
+	struct xarray ref_dplls;
+	char description[DPLL_PIN_DESC_LEN];
+};
+static DEFINE_MUTEX(dpll_device_xa_lock);
+
+static DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
+#define DPLL_REGISTERED		XA_MARK_1
+#define PIN_REGISTERED		XA_MARK_1
+
+#define ASSERT_DPLL_REGISTERED(d)                                           \
+	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+#define ASSERT_DPLL_NOT_REGISTERED(d)                                      \
+	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+
+struct dpll_pin_ref {
+	struct dpll_device *dpll;
+	struct dpll_pin_ops *ops;
+	void *priv;
+};
+
+/**
+ * dpll_device_get_by_id - find dpll device by it's id
+ * @id: id of searched dpll
+ *
+ * Return: dpll_device struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_device_get_by_id(int id)
+{
+	struct dpll_device *dpll = NULL;
+
+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
+		dpll = xa_load(&dpll_device_xa, id);
+
+	return dpll;
+}
+
+/**
+ * dpll_device_get_by_name - find dpll device by it's id
+ * @name: name of searched dpll
+ *
+ * Return: dpll_device struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_device_get_by_name(const char *name)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
+		if (!strcmp(dev_name(&dpll->dev), name)) {
+			ret = dpll;
+			break;
+		}
+	}
+	mutex_unlock(&dpll_device_xa_lock);
+
+	return ret;
+}
+
+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
+						enum dpll_type type, u8 idx)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
+		if (dpll->clock_id == clock_id) {
+			if (dpll->type == type) {
+				if (dpll->dev_driver_idx == idx) {
+					ret = dpll;
+					break;
+				}
+			}
+		}
+	}
+	mutex_unlock(&dpll_device_xa_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
+
+static void dpll_device_release(struct device *dev)
+{
+	struct dpll_device *dpll;
+
+	mutex_lock(&dpll_device_xa_lock);
+	dpll = to_dpll_device(dev);
+	dpll_device_unregister(dpll);
+	mutex_unlock(&dpll_device_xa_lock);
+	dpll_device_free(dpll);
+}
+
+static struct class dpll_class = {
+	.name = "dpll",
+	.dev_release = dpll_device_release,
+};
+
+struct dpll_device
+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
+		   const u64 clock_id, enum dpll_clock_class clock_class,
+		   u8 dev_driver_idx, void *priv, struct device *parent)
+{
+	struct dpll_device *dpll;
+	int ret;
+
+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
+	if (!dpll)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&dpll->lock);
+	dpll->ops = ops;
+	dpll->dev.class = &dpll_class;
+	dpll->parent = parent;
+	dpll->type = type;
+	dpll->dev_driver_idx = dev_driver_idx;
+	dpll->clock_id = clock_id;
+	dpll->clock_class = clock_class;
+
+	mutex_lock(&dpll_device_xa_lock);
+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
+		       xa_limit_16b, GFP_KERNEL);
+	if (ret)
+		goto error;
+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
+		     dev_driver_idx);
+	dpll->priv = priv;
+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_device_xa_lock);
+	dpll_notify_device_create(dpll);
+
+	return dpll;
+
+error:
+	mutex_unlock(&dpll_device_xa_lock);
+	kfree(dpll);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(dpll_device_alloc);
+
+void dpll_device_free(struct dpll_device *dpll)
+{
+	WARN_ON_ONCE(!dpll);
+	WARN_ON_ONCE(!xa_empty(&dpll->pins));
+	xa_destroy(&dpll->pins);
+	mutex_destroy(&dpll->lock);
+	kfree(dpll);
+}
+EXPORT_SYMBOL_GPL(dpll_device_free);
+
+/**
+ * dpll_device_unregister - unregister dpll device
+ * @dpll: registered dpll pointer
+ *
+ * Note: It does not free the memory
+ */
+void dpll_device_unregister(struct dpll_device *dpll)
+{
+	ASSERT_DPLL_REGISTERED(dpll);
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_erase(&dpll_device_xa, dpll->id);
+	dpll_notify_device_delete(dpll);
+	mutex_unlock(&dpll_device_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_unregister);
+
+/**
+ * dpll_id - return dpll id
+ * @dpll: registered dpll pointer
+ *
+ * Return: dpll id.
+ */
+u32 dpll_id(struct dpll_device *dpll)
+{
+	return dpll->id;
+}
+
+/**
+ * dpll_pin_idx - return index of a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ *
+ * Return: index of a pin or PIN_IDX_INVALID if not found.
+ */
+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	struct dpll_pin *pos;
+	unsigned long index;
+
+	xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED) {
+		if (pos == pin)
+			return pin->idx;
+	}
+
+	return PIN_IDX_INVALID;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_idx);
+
+const char *dpll_dev_name(struct dpll_device *dpll)
+{
+	return dev_name(&dpll->dev);
+}
+
+struct dpll_pin *dpll_pin_alloc(const char *description,
+				const enum dpll_pin_type pin_type)
+{
+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
+
+	if (!pin)
+		return ERR_PTR(-ENOMEM);
+	if (pin_type <= DPLL_PIN_TYPE_UNSPEC ||
+	    pin_type > DPLL_PIN_TYPE_MAX)
+		return ERR_PTR(-EINVAL);
+
+	strncpy(pin->description, description, DPLL_PIN_DESC_LEN);
+	pin->description[DPLL_PIN_DESC_LEN - 1] = '\0';
+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
+	pin->type = pin_type;
+
+	return pin;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_alloc);
+
+static int dpll_alloc_pin_on_xa(struct xarray *pins, struct dpll_pin *pin)
+{
+	struct dpll_pin *pos;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(pins, index, pos) {
+		if (pos == pin ||
+		    !strncmp(pos->description, pin->description,
+			     DPLL_PIN_DESC_LEN))
+			return -EEXIST;
+	}
+
+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
+	if (!ret)
+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);
+
+	return ret;
+}
+
+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device *dpll,
+			    struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref, *pos;
+	unsigned long index;
+	u32 idx;
+
+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
+	if (!ref)
+		return -ENOMEM;
+	ref->dpll = dpll;
+	ref->ops = ops;
+	ref->priv = priv;
+	if (!xa_empty(&pin->ref_dplls)) {
+		xa_for_each(&pin->ref_dplls, index, pos) {
+			if (pos->dpll == ref->dpll)
+				return -EEXIST;
+		}
+	}
+
+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
+}
+
+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device *dpll)
+{
+	struct dpll_pin_ref *pos;
+	unsigned long index;
+
+	xa_for_each(&pin->ref_dplls, index, pos) {
+		if (pos->dpll == dpll) {
+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
+			break;
+		}
+	}
+}
+
+static int pin_deregister_from_xa(struct xarray *xa_pins, struct dpll_pin *pin)
+{
+	struct dpll_pin *pos;
+	unsigned long index;
+
+	xa_for_each(xa_pins, index, pos) {
+		if (pos == pin) {
+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));
+			return 0;
+		}
+	}
+
+	return -ENXIO;
+}
+
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		      struct dpll_pin_ops *ops, void *priv)
+{
+	int ret;
+
+	if (!pin || !ops)
+		return -EINVAL;
+
+	mutex_lock(&dpll->lock);
+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
+	if (!ret) {
+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
+		if (ret)
+			pin_deregister_from_xa(&dpll->pins, pin);
+	}
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_register);
+
+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins, int idx)
+{
+	struct dpll_pin *pos;
+	unsigned long index;
+
+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
+		if (pos->idx == idx)
+			return pos;
+	}
+
+	return NULL;
+}
+
+/**
+ * dpll_pin_get_by_idx - find a pin by its index
+ * @dpll: dpll device pointer
+ * @idx: index of pin
+ *
+ * Allows multiple driver instances using one physical DPLL to find
+ * and share pin already registered with existing dpll device.
+ *
+ * Return: pointer if pin was found, NULL otherwise.
+ */
+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
+{
+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
+}
+
+	struct dpll_pin
+*dpll_pin_get_by_description(struct dpll_device *dpll, const char *description)
+{
+	struct dpll_pin *pos, *pin = NULL;
+	unsigned long index;
+
+	xa_for_each(&dpll->pins, index, pos) {
+		if (!strncmp(pos->description, description,
+			     DPLL_PIN_DESC_LEN)) {
+			pin = pos;
+			break;
+		}
+	}
+
+	return pin;
+}
+
+int
+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
+			 struct dpll_device *dpll,
+			 const char *shared_pin_description,
+			 struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin *pin;
+	int ret;
+
+	mutex_lock(&dpll_pin_owner->lock);
+	pin = dpll_pin_get_by_description(dpll_pin_owner,
+					  shared_pin_description);
+	if (!pin) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+	ret = dpll_pin_register(dpll, pin, ops, priv);
+unlock:
+	mutex_unlock(&dpll_pin_owner->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);
+
+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	int ret = 0;
+
+	if (xa_empty(&dpll->pins))
+		return -ENOENT;
+
+	mutex_lock(&dpll->lock);
+	ret = pin_deregister_from_xa(&dpll->pins, pin);
+	if (!ret)
+		dpll_pin_ref_del(pin, dpll);
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_DEL);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_deregister);
+
+void dpll_pin_free(struct dpll_pin *pin)
+{
+	if (!xa_empty(&pin->ref_dplls))
+		return;
+
+	xa_destroy(&pin->ref_dplls);
+	kfree(pin);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_free);
+
+int dpll_muxed_pin_register(struct dpll_device *dpll,
+			    const char *parent_pin_description,
+			    struct dpll_pin *pin,
+			    struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin *parent_pin;
+	int ret;
+
+	if (!parent_pin_description || !pin)
+		return -EINVAL;
+
+	mutex_lock(&dpll->lock);
+	parent_pin = dpll_pin_get_by_description(dpll, parent_pin_description);
+	if (!parent_pin)
+		return -EINVAL;
+	if (parent_pin->type != DPLL_PIN_TYPE_MUX)
+		return -EPERM;
+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
+	if (!ret)
+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
+	if (!ret)
+		pin->parent_pin = parent_pin;
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
+
+/**
+ * dpll_pin_first - get first registered pin
+ * @dpll: registered dpll pointer
+ * @index: found pin index (out)
+ *
+ * Return: dpll_pin struct if found, NULL otherwise.
+ */
+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long *index)
+{
+	*index = 0;
+
+	return xa_find(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
+}
+
+/**
+ * dpll_pin_next - get next registered pin to the relative pin
+ * @dpll: registered dpll pointer
+ * @index: relative pin index (in and out)
+ *
+ * Return: dpll_pin struct if found, NULL otherwise.
+ */
+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index)
+{
+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
+}
+
+/**
+ * dpll_first - get first registered dpll device
+ * @index: found dpll index (out)
+ *
+ * Return: dpll_device struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_first(unsigned long *index)
+{
+	*index = 0;
+
+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
+}
+
+/**
+ * dpll_pin_next - get next registered dpll device to the relative pin
+ * @index: relative dpll index (in and out)
+ *
+ * Return: dpll_pin struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_next(unsigned long *index)
+{
+	return xa_find_after(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
+}
+
+static struct dpll_pin_ref
+*dpll_pin_find_ref(const struct dpll_device *dpll, const struct dpll_pin *pin)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long index;
+
+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
+		if (ref->dpll != dpll)
+			continue;
+		else
+			return ref;
+	}
+
+	return NULL;
+}
+
+/**
+ * dpll_pin_type_get - get type of a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @type: on success - configured pin type
+ *
+ * Return:
+ * * 0 - successfully got pin's type
+ * * negative - failed to get pin's type
+ */
+int dpll_pin_type_get(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin,
+		      enum dpll_pin_type *type)
+{
+	if (!pin)
+		return -ENODEV;
+	*type = pin->type;
+
+	return 0;
+}
+
+/**
+ * dpll_pin_signal_type_get - get signal type of a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @type: on success - configured signal type
+ *
+ * Return:
+ * * 0 - successfully got signal type
+ * * negative - failed to obtain signal type
+ */
+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     enum dpll_pin_signal_type *type)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->signal_type_get)
+		return -EOPNOTSUPP;
+	ret = ref->ops->signal_type_get(ref->dpll, pin, type);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_signal_type_set - set signal type of a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @type: type to be set
+ *
+ * Return:
+ * * 0 - signal type set
+ * * negative - failed to set signal type
+ */
+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     const enum dpll_pin_signal_type type)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long index;
+	int ret;
+
+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
+		if (!ref->dpll)
+			return -EFAULT;
+		if (!ref || !ref->ops || !ref->ops->signal_type_set)
+			return -EOPNOTSUPP;
+		if (ref->dpll != dpll)
+			mutex_lock(&ref->dpll->lock);
+		ret = ref->ops->signal_type_set(ref->dpll, pin, type);
+		if (ref->dpll != dpll)
+			mutex_unlock(&ref->dpll->lock);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * dpll_pin_signal_type_supported - check if signal type is supported on a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @type: type being checked
+ * @supported: on success - if given signal type is supported
+ *
+ * Return:
+ * * 0 - successfully got supported signal type
+ * * negative - failed to obtain supported signal type
+ */
+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
+				   const struct dpll_pin *pin,
+				   const enum dpll_pin_signal_type type,
+				   bool *supported)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->signal_type_supported)
+		return -EOPNOTSUPP;
+	*supported = ref->ops->signal_type_supported(ref->dpll, pin, type);
+
+	return 0;
+}
+
+/**
+ * dpll_pin_mode_active - check if given mode is active on a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @mode: mode being checked
+ * @active: on success - if mode is active
+ *
+ * Return:
+ * * 0 - successfully checked if mode is active
+ * * negative - failed to check for active mode
+ */
+int dpll_pin_mode_active(const struct dpll_device *dpll,
+			  const struct dpll_pin *pin,
+			  const enum dpll_pin_mode mode,
+			  bool *active)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->mode_active)
+		return -EOPNOTSUPP;
+	*active = ref->ops->mode_active(ref->dpll, pin, mode);
+
+	return 0;
+}
+
+/**
+ * dpll_pin_mode_supported - check if given mode is supported on a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @mode: mode being checked
+ * @supported: on success - if mode is supported
+ *
+ * Return:
+ * * 0 - successfully checked if mode is supported
+ * * negative - failed to check for supported mode
+ */
+int dpll_pin_mode_supported(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     const enum dpll_pin_mode mode,
+			     bool *supported)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->mode_supported)
+		return -EOPNOTSUPP;
+	*supported = ref->ops->mode_supported(ref->dpll, pin, mode);
+
+	return 0;
+}
+
+/**
+ * dpll_pin_mode_set - set pin's mode
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @mode: mode being set
+ *
+ * Return:
+ * * 0 - successfully set the mode
+ * * negative - failed to set the mode
+ */
+int dpll_pin_mode_set(const struct dpll_device *dpll,
+		       const struct dpll_pin *pin,
+		       const enum dpll_pin_mode mode)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long index;
+	int ret;
+
+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
+		if (!ref)
+			return -ENODEV;
+		if (!ref->ops || !ref->ops->mode_enable)
+			return -EOPNOTSUPP;
+		if (ref->dpll != dpll)
+			mutex_lock(&ref->dpll->lock);
+		ret = ref->ops->mode_enable(ref->dpll, pin, mode);
+		if (ref->dpll != dpll)
+			mutex_unlock(&ref->dpll->lock);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * dpll_pin_custom_freq_get - get pin's custom frequency
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @freq: on success - custom frequency of a pin
+ *
+ * Return:
+ * * 0 - successfully got custom frequency
+ * * negative - failed to obtain custom frequency
+ */
+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin, u32 *freq)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->custom_freq_get)
+		return -EOPNOTSUPP;
+	ret = ref->ops->custom_freq_get(ref->dpll, pin, freq);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_custom_freq_set - set pin's custom frequency
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @freq: custom frequency to be set
+ *
+ * Return:
+ * * 0 - successfully set custom frequency
+ * * negative - failed to set custom frequency
+ */
+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin, const u32 freq)
+{
+	enum dpll_pin_signal_type type;
+	struct dpll_pin_ref *ref;
+	unsigned long index;
+	int ret;
+
+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
+		if (!ref)
+			return -ENODEV;
+		if (!ref->ops || !ref->ops->custom_freq_set ||
+		    !ref->ops->signal_type_get)
+			return -EOPNOTSUPP;
+		if (dpll != ref->dpll)
+			mutex_lock(&ref->dpll->lock);
+		ret = ref->ops->signal_type_get(dpll, pin, &type);
+		if (!ret && type == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ)
+			ret = ref->ops->custom_freq_set(ref->dpll, pin, freq);
+		if (dpll != ref->dpll)
+			mutex_unlock(&ref->dpll->lock);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * dpll_pin_prio_get - get pin's prio on dpll
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @prio: on success - priority of a pin on a dpll
+ *
+ * Return:
+ * * 0 - successfully got priority
+ * * negative - failed to obtain priority
+ */
+int dpll_pin_prio_get(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin, u32 *prio)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->prio_get)
+		return -EOPNOTSUPP;
+	ret = ref->ops->prio_get(ref->dpll, pin, prio);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_prio_set - set pin's prio on dpll
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @prio: priority of a pin to be set on a dpll
+ *
+ * Return:
+ * * 0 - successfully set priority
+ * * negative - failed to set the priority
+ */
+int dpll_pin_prio_set(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin, const u32 prio)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->prio_set)
+		return -EOPNOTSUPP;
+	ret = ref->ops->prio_set(ref->dpll, pin, prio);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_netifindex_get - get pin's netdev iterface index
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @netifindex: on success - index of a netdevice associated with pin
+ *
+ * Return:
+ * * 0 - successfully got netdev interface index
+ * * negative - failed to obtain netdev interface index
+ */
+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin,
+			    int *netifindex)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->net_if_idx_get)
+		return -EOPNOTSUPP;
+	ret = ref->ops->net_if_idx_get(ref->dpll, pin, netifindex);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_description - provide pin's description string
+ * @pin: registered pin pointer
+ *
+ * Return: pointer to a description string.
+ */
+const char *dpll_pin_description(struct dpll_pin *pin)
+{
+	return pin->description;
+}
+
+/**
+ * dpll_pin_parent - provide pin's parent pin if available
+ * @pin: registered pin pointer
+ *
+ * Return: pointer to aparent if found, NULL otherwise.
+ */
+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)
+{
+	return pin->parent_pin;
+}
+
+/**
+ * dpll_mode_set - handler for dpll mode set
+ * @dpll: registered dpll pointer
+ * @mode: mode to be set
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode)
+{
+	int ret;
+
+	if (!dpll->ops || !dpll->ops)
+		return -EOPNOTSUPP;
+
+	ret = dpll->ops->mode_set(dpll, mode);
+
+	return ret;
+}
+
+/**
+ * dpll_source_idx_set - handler for selecting a dpll's source
+ * @dpll: registered dpll pointer
+ * @source_pin_idx: index of a source pin to e selected
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_source_idx_set(struct dpll_device *dpll, const u32 source_pin_idx)
+{
+	struct dpll_pin_ref *ref;
+	struct dpll_pin *pin;
+	int ret;
+
+	pin = dpll_pin_get_by_idx_from_xa(&dpll->pins, source_pin_idx);
+	if (!pin)
+		return -ENXIO;
+	ref = dpll_pin_find_ref(dpll, pin);
+	if (!ref || !ref->ops)
+		return -EFAULT;
+	if (!ref->ops->select)
+		return -ENODEV;
+	ret = ref->ops->select(ref->dpll, pin);
+
+	return ret;
+}
+
+/**
+ * dpll_lock - locks the dpll using internal mutex
+ * @dpll: registered dpll pointer
+ */
+void dpll_lock(struct dpll_device *dpll)
+{
+	mutex_lock(&dpll->lock);
+}
+
+/**
+ * dpll_unlock - unlocks the dpll using internal mutex
+ * @dpll: registered dpll pointer
+ */
+void dpll_unlock(struct dpll_device *dpll)
+{
+	mutex_unlock(&dpll->lock);
+}
+
+enum dpll_pin_type dpll_pin_type(const struct dpll_pin *pin)
+{
+	return pin->type;
+}
+
+void *dpll_priv(const struct dpll_device *dpll)
+{
+	return dpll->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_priv);
+
+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin *pin)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+
+	if (!ref)
+		return NULL;
+
+	return ref->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_priv);
+
+static int __init dpll_init(void)
+{
+	int ret;
+
+	ret = dpll_netlink_init();
+	if (ret)
+		goto error;
+
+	ret = class_register(&dpll_class);
+	if (ret)
+		goto unregister_netlink;
+
+	return 0;
+
+unregister_netlink:
+	dpll_netlink_finish();
+error:
+	mutex_destroy(&dpll_device_xa_lock);
+	return ret;
+}
+subsys_initcall(dpll_init);
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
new file mode 100644
index 000000000000..b933d63b60c1
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+#ifndef __DPLL_CORE_H__
+#define __DPLL_CORE_H__
+
+#include <linux/dpll.h>
+
+#include "dpll_netlink.h"
+
+#define to_dpll_device(_dev) \
+	container_of(_dev, struct dpll_device, dev)
+
+/**
+ * struct dpll_device - structure for a DPLL device
+ * @id:		unique id number for each device
+ * @dev:	struct device for this dpll device
+ * @parent:	parent device
+ * @ops:	operations this &dpll_device supports
+ * @lock:	mutex to serialize operations
+ * @type:	type of a dpll
+ * @priv:	pointer to private information of owner
+ * @pins:	list of pointers to pins registered with this dpll
+ * @clock_id:	unique identifier (clock_id) of a dpll
+ * @clock_class	quality class of a DPLL clock
+ * @dev_driver_idx: provided by driver for
+ */
+struct dpll_device {
+	u32 id;
+	struct device dev;
+	struct device *parent;
+	struct dpll_device_ops *ops;
+	struct mutex lock;
+	enum dpll_type type;
+	void *priv;
+	struct xarray pins;
+	u64 clock_id;
+	enum dpll_clock_class clock_class;
+	u8 dev_driver_idx;
+};
+
+#define for_each_pin_on_dpll(dpll, pin, i)			\
+	for (pin = dpll_pin_first(dpll, &i); pin != NULL;	\
+	     pin = dpll_pin_next(dpll, &i))
+
+#define for_each_dpll(dpll, i)                         \
+	for (dpll = dpll_first(&i); dpll != NULL; dpll = dpll_next(&i))
+
+struct dpll_device *dpll_device_get_by_id(int id);
+
+struct dpll_device *dpll_device_get_by_name(const char *name);
+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long *index);
+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index);
+struct dpll_device *dpll_first(unsigned long *index);
+struct dpll_device *dpll_next(unsigned long *index);
+void dpll_device_unregister(struct dpll_device *dpll);
+u32 dpll_id(struct dpll_device *dpll);
+const char *dpll_dev_name(struct dpll_device *dpll);
+void dpll_lock(struct dpll_device *dpll);
+void dpll_unlock(struct dpll_device *dpll);
+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
+int dpll_pin_type_get(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin,
+		      enum dpll_pin_type *type);
+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     enum dpll_pin_signal_type *type);
+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     const enum dpll_pin_signal_type type);
+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
+				   const struct dpll_pin *pin,
+				   const enum dpll_pin_signal_type type,
+				   bool *supported);
+int dpll_pin_mode_active(const struct dpll_device *dpll,
+			 const struct dpll_pin *pin,
+			 const enum dpll_pin_mode mode,
+			 bool *active);
+int dpll_pin_mode_supported(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin,
+			    const enum dpll_pin_mode mode,
+			    bool *supported);
+int dpll_pin_mode_set(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin,
+		      const enum dpll_pin_mode mode);
+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin, u32 *freq);
+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin, const u32 freq);
+int dpll_pin_prio_get(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin, u32 *prio);
+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx);
+int dpll_pin_prio_set(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin, const u32 prio);
+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin,
+			    int *netifindex);
+const char *dpll_pin_description(struct dpll_pin *pin);
+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin);
+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode);
+int dpll_source_idx_set(struct dpll_device *dpll, const u32 source_pin_idx);
+
+#endif
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..91a1e5025ab2
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,883 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic netlink for DPLL management framework
+ *
+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include "dpll_core.h"
+
+#include <uapi/linux/dpll.h>
+
+static const struct genl_multicast_group dpll_mcgrps[] = {
+	{ .name = DPLL_MONITOR_GROUP_NAME,  },
+};
+
+static const struct nla_policy dpll_cmd_device_get_policy[] = {
+	[DPLLA_ID]		= { .type = NLA_U32 },
+	[DPLLA_NAME]		= { .type = NLA_STRING,
+				    .len = DPLL_NAME_LEN },
+	[DPLLA_FILTER]		= { .type = NLA_U32 },
+};
+
+static const struct nla_policy dpll_cmd_device_set_policy[] = {
+	[DPLLA_ID]		= { .type = NLA_U32 },
+	[DPLLA_NAME]		= { .type = NLA_STRING,
+				    .len = DPLL_NAME_LEN },
+	[DPLLA_MODE]		= { .type = NLA_U32 },
+	[DPLLA_SOURCE_PIN_IDX]	= { .type = NLA_U32 },
+};
+
+static const struct nla_policy dpll_cmd_pin_set_policy[] = {
+	[DPLLA_ID]		= { .type = NLA_U32 },
+	[DPLLA_PIN_IDX]		= { .type = NLA_U32 },
+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
+	[DPLLA_PIN_MODE]	= { .type = NLA_U32 },
+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
+};
+
+struct dpll_dump_ctx {
+	int dump_filter;
+};
+
+static struct genl_family dpll_gnl_family;
+
+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
+{
+	return (struct dpll_dump_ctx *)cb->ctx;
+}
+
+static int dpll_msg_add_id(struct sk_buff *msg, u32 id)
+{
+	if (nla_put_u32(msg, DPLLA_ID, id))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_name(struct sk_buff *msg, const char *name)
+{
+	if (nla_put_string(msg, DPLLA_NAME, name))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int __dpll_msg_add_mode(struct sk_buff *msg, enum dplla msg_type,
+			       enum dpll_mode mode)
+{
+	if (nla_put_s32(msg, msg_type, mode))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll)
+{
+	enum dpll_mode m;
+	int ret;
+
+	if (!dpll->ops->mode_get)
+		return 0;
+	ret = dpll->ops->mode_get(dpll, &m);
+	if (ret)
+		return ret;
+
+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
+}
+
+static int
+dpll_msg_add_modes_supported(struct sk_buff *msg,
+			     const struct dpll_device *dpll)
+{
+	enum dpll_mode i;
+	int ret = 0;
+
+	if (!dpll->ops->mode_supported)
+		return ret;
+
+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
+		if (dpll->ops->mode_supported(dpll, i)) {
+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int dpll_msg_add_clock_id(struct sk_buff *msg,
+				 const struct dpll_device *dpll)
+{
+	if (nla_put_64bit(msg, DPLLA_CLOCK_ID, sizeof(dpll->clock_id),
+			  &dpll->clock_id, 0))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_clock_class(struct sk_buff *msg,
+				    const struct dpll_device *dpll)
+{
+	if (nla_put_s32(msg, DPLLA_CLOCK_CLASS, dpll->clock_class))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	u32 source_idx;
+	int ret;
+
+	if (!dpll->ops->source_pin_idx_get)
+		return 0;
+	ret = dpll->ops->source_pin_idx_get(dpll, &source_idx);
+	if (ret)
+		return ret;
+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	enum dpll_lock_status s;
+	int ret;
+
+	if (!dpll->ops->lock_status_get)
+		return 0;
+	ret = dpll->ops->lock_status_get(dpll, &s);
+	if (ret)
+		return ret;
+	if (nla_put_s32(msg, DPLLA_LOCK_STATUS, s))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	s32 temp;
+	int ret;
+
+	if (!dpll->ops->temp_get)
+		return 0;
+	ret = dpll->ops->temp_get(dpll, &temp);
+	if (ret)
+		return ret;
+	if (nla_put_u32(msg, DPLLA_TEMP, temp))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_idx(struct sk_buff *msg, u32 pin_idx)
+{
+	if (nla_put_u32(msg, DPLLA_PIN_IDX, pin_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_description(struct sk_buff *msg,
+					const char *description)
+{
+	if (nla_put_string(msg, DPLLA_PIN_DESCRIPTION, description))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_parent_idx(struct sk_buff *msg, u32 parent_idx)
+{
+	if (nla_put_u32(msg, DPLLA_PIN_PARENT_IDX, parent_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_device *dpll,
+		      const struct dpll_pin *pin)
+{
+	enum dpll_pin_type t;
+
+	if (dpll_pin_type_get(dpll, pin, &t))
+		return 0;
+
+	if (nla_put_s32(msg, DPLLA_PIN_TYPE, t))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int __dpll_msg_add_pin_signal_type(struct sk_buff *msg,
+					  enum dplla attr,
+					  enum dpll_pin_signal_type type)
+{
+	if (nla_put_s32(msg, attr, type))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_signal_type(struct sk_buff *msg,
+					const struct dpll_device *dpll,
+					const struct dpll_pin *pin)
+{
+	enum dpll_pin_signal_type t;
+	int ret;
+
+	if (dpll_pin_signal_type_get(dpll, pin, &t))
+		return 0;
+	ret = __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
+	if (ret)
+		return ret;
+
+	if (t == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) {
+		u32 freq;
+
+		if (dpll_pin_custom_freq_get(dpll, pin, &freq))
+			return 0;
+		if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
+			return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
+					const struct dpll_device *dpll,
+					const struct dpll_pin *pin)
+{
+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
+	enum dpll_pin_signal_type i;
+	bool supported;
+
+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
+		if (dpll_pin_signal_type_supported(dpll, pin, i, &supported))
+			continue;
+		if (supported) {
+			int ret = __dpll_msg_add_pin_signal_type(msg, da, i);
+
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
+				   const struct dpll_device *dpll,
+				   const struct dpll_pin *pin)
+{
+	enum dpll_pin_mode i;
+	bool active;
+
+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
+		if (dpll_pin_mode_active(dpll, pin, i, &active))
+			return 0;
+		if (active)
+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
+				return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_modes_supported(struct sk_buff *msg,
+					     const struct dpll_device *dpll,
+					     const struct dpll_pin *pin)
+{
+	enum dpll_pin_mode i;
+	bool supported;
+
+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
+		if (dpll_pin_mode_supported(dpll, pin, i, &supported))
+			return 0;
+		if (supported)
+			if (nla_put_s32(msg, DPLLA_PIN_MODE_SUPPORTED, i))
+				return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_device *dpll,
+		      const struct dpll_pin *pin)
+{
+	u32 prio;
+
+	if (dpll_pin_prio_get(dpll, pin, &prio))
+		return 0;
+	if (nla_put_u32(msg, DPLLA_PIN_PRIO, prio))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct dpll_device *dpll,
+			    const struct dpll_pin *pin)
+{
+	int netifindex;
+
+	if (dpll_pin_netifindex_get(dpll, pin, &netifindex))
+		return 0;
+	if (nla_put_s32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+__dpll_cmd_device_dump_one(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
+
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
+
+	return ret;
+}
+
+static int
+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_device *dpll,
+			struct dpll_pin *pin)
+{
+	struct dpll_pin *parent = NULL;
+	int ret;
+
+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_description(msg, dpll_pin_description(pin));
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_type(msg, dpll, pin);
+	if (ret)
+		return ret;
+	parent = dpll_pin_parent(pin);
+	if (parent) {
+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
+								    parent));
+		if (ret)
+			return ret;
+	}
+	ret = dpll_msg_add_pin_signal_type(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_signal_types_supported(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_modes(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_modes_supported(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_prio(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_netifindex(msg, dpll, pin);
+
+	return ret;
+}
+
+static int __dpll_cmd_dump_pins(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	struct dpll_pin *pin;
+	struct nlattr *attr;
+	unsigned long i;
+	int ret = 0;
+
+	for_each_pin_on_dpll(dpll, pin, i) {
+		attr = nla_nest_start(msg, DPLLA_PIN);
+		if (!attr) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		ret = __dpll_cmd_pin_dump_one(msg, dpll, pin);
+		if (ret)
+			goto nest_cancel;
+		nla_nest_end(msg, attr);
+	}
+
+	return ret;
+
+nest_cancel:
+	nla_nest_cancel(msg, attr);
+	return ret;
+}
+
+static int
+__dpll_cmd_dump_status(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	int ret = dpll_msg_add_source_pin(msg, dpll);
+
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_temp(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_lock_status(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_mode(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_modes_supported(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_clock_id(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_clock_class(msg, dpll);
+
+	return ret;
+}
+
+static int
+dpll_device_dump_one(struct dpll_device *dpll, struct sk_buff *msg,
+		     int dump_filter)
+{
+	int ret;
+
+	dpll_lock(dpll);
+	ret = __dpll_cmd_device_dump_one(msg, dpll);
+	if (ret)
+		goto out_unlock;
+
+	if (dump_filter & DPLL_FILTER_STATUS) {
+		ret = __dpll_cmd_dump_status(msg, dpll);
+		if (ret) {
+			if (ret != -EMSGSIZE)
+				ret = -EAGAIN;
+			goto out_unlock;
+		}
+	}
+	if (dump_filter & DPLL_FILTER_PINS)
+		ret = __dpll_cmd_dump_pins(msg, dpll);
+	dpll_unlock(dpll);
+
+	return ret;
+out_unlock:
+	dpll_unlock(dpll);
+	return ret;
+}
+
+static int
+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
+			 struct dpll_pin *pin, struct genl_info *info)
+{
+	enum dpll_pin_signal_type st;
+	enum dpll_pin_mode mode;
+	struct nlattr *a;
+	int rem, ret = 0;
+	u32 prio, freq;
+
+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(a)) {
+		case DPLLA_PIN_SIGNAL_TYPE:
+			st = nla_get_s32(a);
+			ret = dpll_pin_signal_type_set(dpll, pin, st);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_CUSTOM_FREQ:
+			freq = nla_get_u32(a);
+			ret = dpll_pin_custom_freq_set(dpll, pin, freq);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_MODE:
+			mode = nla_get_s32(a);
+			ret = dpll_pin_mode_set(dpll, pin, mode);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_PRIO:
+			prio = nla_get_u32(a);
+			ret = dpll_pin_prio_set(dpll, pin, prio);
+			if (ret)
+				return ret;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int dpll_cmd_pin_set(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct nlattr **attrs = info->attrs;
+	struct dpll_pin *pin;
+	int pin_id;
+
+	if (!attrs[DPLLA_PIN_IDX])
+		return -EINVAL;
+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
+	dpll_lock(dpll);
+	pin = dpll_pin_get_by_idx(dpll, pin_id);
+	dpll_unlock(dpll);
+	if (!pin)
+		return -ENODEV;
+	return dpll_pin_set_from_nlattr(dpll, pin, info);
+}
+
+enum dpll_mode dpll_msg_read_mode(struct nlattr *a)
+{
+	return nla_get_s32(a);
+}
+
+u32 dpll_msg_read_source_pin_id(struct nlattr *a)
+{
+	return nla_get_u32(a);
+}
+
+static int
+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
+{
+	enum dpll_mode m;
+	struct nlattr *a;
+	int rem, ret = 0;
+	u32 source_pin;
+
+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(a)) {
+		case DPLLA_MODE:
+			m = dpll_msg_read_mode(a);
+
+			ret = dpll_mode_set(dpll, m);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_SOURCE_PIN_IDX:
+			source_pin = dpll_msg_read_source_pin_id(a);
+
+			ret = dpll_source_idx_set(dpll, source_pin);
+			if (ret)
+				return ret;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int dpll_cmd_device_set(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+
+	return dpll_set_from_nlattr(dpll, info);
+}
+
+static int
+dpll_cmd_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+	struct dpll_device *dpll;
+	struct nlattr *hdr;
+	unsigned long i;
+	int ret;
+
+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+			  &dpll_gnl_family, 0, DPLL_CMD_DEVICE_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	for_each_dpll(dpll, i) {
+		ret = dpll_device_dump_one(dpll, skb, ctx->dump_filter);
+		if (ret)
+			break;
+	}
+
+	if (ret)
+		genlmsg_cancel(skb, hdr);
+	else
+		genlmsg_end(skb, hdr);
+
+	return ret;
+}
+
+static int dpll_cmd_device_get(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct nlattr **attrs = info->attrs;
+	struct sk_buff *msg;
+	int dump_filter = 0;
+	struct nlattr *hdr;
+	int ret;
+
+	if (attrs[DPLLA_FILTER])
+		dump_filter = nla_get_s32(attrs[DPLLA_FILTER]);
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_gnl_family, 0,
+				DPLL_CMD_DEVICE_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	ret = dpll_device_dump_one(dpll, msg, dump_filter);
+	if (ret)
+		goto out_free_msg;
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+
+out_free_msg:
+	nlmsg_free(msg);
+	return ret;
+
+}
+
+static int dpll_cmd_device_get_start(struct netlink_callback *cb)
+{
+	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+	struct nlattr *attr = info->attrs[DPLLA_FILTER];
+
+	if (attr)
+		ctx->dump_filter = nla_get_s32(attr);
+	else
+		ctx->dump_filter = 0;
+
+	return 0;
+}
+
+static int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			 struct genl_info *info)
+{
+	struct dpll_device *dpll_id = NULL, *dpll_name = NULL;
+
+	if (!info->attrs[DPLLA_ID] &&
+	    !info->attrs[DPLLA_NAME])
+		return -EINVAL;
+
+	if (info->attrs[DPLLA_ID]) {
+		u32 id = nla_get_u32(info->attrs[DPLLA_ID]);
+
+		dpll_id = dpll_device_get_by_id(id);
+		if (!dpll_id)
+			return -ENODEV;
+		info->user_ptr[0] = dpll_id;
+	}
+	if (info->attrs[DPLLA_NAME]) {
+		const char *name = nla_data(info->attrs[DPLLA_NAME]);
+
+		dpll_name = dpll_device_get_by_name(name);
+		if (!dpll_name)
+			return -ENODEV;
+
+		if (dpll_id && dpll_name != dpll_id)
+			return -EINVAL;
+		info->user_ptr[0] = dpll_name;
+	}
+
+	return 0;
+}
+
+static const struct genl_ops dpll_ops[] = {
+	{
+		.cmd	= DPLL_CMD_DEVICE_GET,
+		.flags  = GENL_UNS_ADMIN_PERM,
+		.start	= dpll_cmd_device_get_start,
+		.dumpit	= dpll_cmd_device_dump,
+		.doit	= dpll_cmd_device_get,
+		.policy	= dpll_cmd_device_get_policy,
+		.maxattr = ARRAY_SIZE(dpll_cmd_device_get_policy) - 1,
+	},
+	{
+		.cmd	= DPLL_CMD_DEVICE_SET,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= dpll_cmd_device_set,
+		.policy	= dpll_cmd_device_set_policy,
+		.maxattr = ARRAY_SIZE(dpll_cmd_device_set_policy) - 1,
+	},
+	{
+		.cmd	= DPLL_CMD_PIN_SET,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= dpll_cmd_pin_set,
+		.policy	= dpll_cmd_pin_set_policy,
+		.maxattr = ARRAY_SIZE(dpll_cmd_pin_set_policy) - 1,
+	},
+};
+
+static struct genl_family dpll_family __ro_after_init = {
+	.hdrsize	= 0,
+	.name		= DPLL_FAMILY_NAME,
+	.version	= DPLL_VERSION,
+	.ops		= dpll_ops,
+	.n_ops		= ARRAY_SIZE(dpll_ops),
+	.mcgrps		= dpll_mcgrps,
+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
+	.pre_doit	= dpll_pre_doit,
+	.parallel_ops   = true,
+};
+
+static int dpll_event_device_id(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
+
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
+
+	return ret;
+}
+
+static int dpll_event_device_change(struct sk_buff *msg,
+				    struct dpll_device *dpll,
+				    struct dpll_pin *pin,
+				    enum dpll_event_change event)
+{
+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
+
+	if (ret)
+		return ret;
+	ret = nla_put_s32(msg, DPLLA_CHANGE_TYPE, event);
+	if (ret)
+		return ret;
+	switch (event)	{
+	case DPLL_CHANGE_PIN_ADD:
+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
+	case DPLL_CHANGE_PIN_MODE:
+	case DPLL_CHANGE_PIN_PRIO:
+		ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * Generic netlink DPLL event encoding
+ */
+static int dpll_send_event_create(enum dpll_event event,
+				  struct dpll_device *dpll)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
+	if (!hdr)
+		goto out_free_msg;
+
+	ret = dpll_event_device_id(msg, dpll);
+	if (ret)
+		goto out_cancel_msg;
+	genlmsg_end(msg, hdr);
+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+out_cancel_msg:
+	genlmsg_cancel(msg, hdr);
+out_free_msg:
+	nlmsg_free(msg);
+
+	return ret;
+}
+
+/*
+ * Generic netlink DPLL event encoding
+ */
+static int dpll_send_event_change(struct dpll_device *dpll,
+				  struct dpll_pin *pin,
+				  enum dpll_event_change event)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, DPLL_EVENT_DEVICE_CHANGE);
+	if (!hdr)
+		goto out_free_msg;
+
+	ret = dpll_event_device_change(msg, dpll, pin, event);
+	if (ret)
+		goto out_cancel_msg;
+	genlmsg_end(msg, hdr);
+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+out_cancel_msg:
+	genlmsg_cancel(msg, hdr);
+out_free_msg:
+	nlmsg_free(msg);
+
+	return ret;
+}
+
+int dpll_notify_device_create(struct dpll_device *dpll)
+{
+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
+}
+
+int dpll_notify_device_delete(struct dpll_device *dpll)
+{
+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
+}
+
+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change event)
+{
+	return dpll_send_event_change(dpll, NULL, event);
+}
+EXPORT_SYMBOL_GPL(dpll_device_notify);
+
+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dpll_event_change event)
+{
+	return dpll_send_event_change(dpll, pin, event);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_notify);
+
+int __init dpll_netlink_init(void)
+{
+	return genl_register_family(&dpll_family);
+}
+
+void dpll_netlink_finish(void)
+{
+	genl_unregister_family(&dpll_family);
+}
+
+void __exit dpll_netlink_fini(void)
+{
+	dpll_netlink_finish();
+}
diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
new file mode 100644
index 000000000000..8e50b2493027
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+/**
+ * dpll_notify_device_create - notify that the device has been created
+ * @dpll: registered dpll pointer
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_notify_device_create(struct dpll_device *dpll);
+
+
+/**
+ * dpll_notify_device_delete - notify that the device has been deleted
+ * @dpll: registered dpll pointer
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_notify_device_delete(struct dpll_device *dpll);
+
+int __init dpll_netlink_init(void);
+void dpll_netlink_finish(void);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
new file mode 100644
index 000000000000..fcba46ea1b7b
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+#ifndef __DPLL_H__
+#define __DPLL_H__
+
+#include <uapi/linux/dpll.h>
+#include <linux/device.h>
+
+struct dpll_device;
+struct dpll_pin;
+
+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
+
+struct dpll_device_ops {
+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode);
+	int (*mode_set)(const struct dpll_device *dpll,
+			const enum dpll_mode mode);
+	bool (*mode_supported)(const struct dpll_device *dpll,
+			       const enum dpll_mode mode);
+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
+				  u32 *pin_idx);
+	int (*lock_status_get)(const struct dpll_device *dpll,
+			       enum dpll_lock_status *status);
+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp);
+};
+
+struct dpll_pin_ops {
+	int (*signal_type_get)(const struct dpll_device *dpll,
+			       const struct dpll_pin *pin,
+			       enum dpll_pin_signal_type *type);
+	int (*signal_type_set)(const struct dpll_device *dpll,
+			       const struct dpll_pin *pin,
+			       const enum dpll_pin_signal_type type);
+	bool (*signal_type_supported)(const struct dpll_device *dpll,
+				      const struct dpll_pin *pin,
+				      const enum dpll_pin_signal_type type);
+	int (*custom_freq_set)(const struct dpll_device *dpll,
+			       const struct dpll_pin *pin,
+			       const u32 custom_freq);
+	int (*custom_freq_get)(const struct dpll_device *dpll,
+			       const struct dpll_pin *pin,
+			       u32 *custom_freq);
+	bool (*mode_active)(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     const enum dpll_pin_mode mode);
+	int (*mode_enable)(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin,
+			    const enum dpll_pin_mode mode);
+	bool (*mode_supported)(const struct dpll_device *dpll,
+				const struct dpll_pin *pin,
+				const enum dpll_pin_mode mode);
+	int (*prio_get)(const struct dpll_device *dpll,
+			const struct dpll_pin *pin,
+			u32 *prio);
+	int (*prio_set)(const struct dpll_device *dpll,
+			const struct dpll_pin *pin,
+			const u32 prio);
+	int (*net_if_idx_get)(const struct dpll_device *dpll,
+			      const struct dpll_pin *pin,
+			      int *net_if_idx);
+	int (*select)(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin);
+};
+
+/**
+ * dpll_device_alloc - allocate memory for a new dpll_device object
+ * @ops: pointer to dpll operations structure
+ * @type: type of a dpll being allocated
+ * @clock_id: a system unique number for a device
+ * @clock_class: quality class of a DPLL clock
+ * @dev_driver_idx: index of dpll device on parent device
+ * @priv: private data of a registerer
+ * @parent: device structure of a module registering dpll device
+ *
+ * Allocate memory for a new dpll and initialize it with its type, name,
+ * callbacks and private data pointer.
+ *
+ * Name is generated based on: parent driver, type and dev_driver_idx.
+ * Finding allocated and registered dpll device is also possible with
+ * the: clock_id, type and dev_driver_idx. This way dpll device can be
+ * shared by multiple instances of a device driver.
+ *
+ * Returns:
+ * * pointer to initialized dpll - success
+ * * NULL - memory allocation fail
+ */
+struct dpll_device
+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
+		   const u64 clock_id, enum dpll_clock_class clock_class,
+		   u8 dev_driver_idx, void *priv, struct device *parent);
+
+/**
+ * dpll_device_unregister - unregister registered dpll
+ * @dpll: pointer to dpll
+ *
+ * Unregister the dpll from the subsystem, make it unavailable for netlink
+ * API users.
+ */
+void dpll_device_unregister(struct dpll_device *dpll);
+
+/**
+ * dpll_device_free - free dpll memory
+ * @dpll: pointer to dpll
+ *
+ * Free memory allocated with ``dpll_device_alloc(..)``
+ */
+void dpll_device_free(struct dpll_device *dpll);
+
+/**
+ * dpll_priv - get private data
+ * @dpll: pointer to dpll
+ *
+ * Obtain private data pointer passed to dpll subsystem when allocating
+ * device with ``dpll_device_alloc(..)``
+ */
+void *dpll_priv(const struct dpll_device *dpll);
+
+/**
+ * dpll_pin_priv - get private data
+ * @dpll: pointer to dpll
+ *
+ * Obtain private pin data pointer passed to dpll subsystem when pin
+ * was registered with dpll.
+ */
+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
+
+/**
+ * dpll_pin_idx - get pin idx
+ * @dpll: pointer to dpll
+ * @pin: pointer to a pin
+ *
+ * Obtain pin index of given pin on given dpll.
+ *
+ * Return: PIN_IDX_INVALID - if failed to find pin, otherwise pin index
+ */
+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
+
+/**
+ * dpll_shared_pin_register - share a pin between dpll devices
+ * @dpll_pin_owner: a dpll already registered with a pin
+ * @dpll: a dpll being registered with a pin
+ * @shared_pin_description: identifies pin registered with dpll device
+ *	(@dpll_pin_owner) which is now being registered with new dpll (@dpll)
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ *
+ * Register a pin already registered with different dpll device.
+ * Allow to share a single pin within multiple dpll instances.
+ *
+ * Returns:
+ * * 0 - success
+ * * negative - failure
+ */
+int
+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
+			 struct dpll_device *dpll,
+			 const char *shared_pin_description,
+			 struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_pin_alloc - allocate memory for a new dpll_pin object
+ * @description: pointer to string description of a pin with max length
+ * equal to PIN_DESC_LEN
+ * @type: type of allocated pin
+ *
+ * Allocate memory for a new pin and initialize its resources.
+ *
+ * Returns:
+ * * pointer to initialized pin - success
+ * * NULL - memory allocation fail
+ */
+struct dpll_pin *dpll_pin_alloc(const char *description,
+				const enum dpll_pin_type type);
+
+/**
+ * dpll_pin_register - register pin with a dpll device
+ * @dpll: pointer to dpll object to register pin with
+ * @pin: pointer to allocated pin object being registered with dpll
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ *
+ * Register previously allocated pin object with a dpll device.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this dpll,
+ * * -EBUSY - couldn't allocate id for a pin.
+ */
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		      struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_pin_deregister - deregister pin from a dpll device
+ * @dpll: pointer to dpll object to deregister pin from
+ * @pin: pointer to allocated pin object being deregistered from dpll
+ *
+ * Deregister previously registered pin object from a dpll device.
+ *
+ * Return:
+ * * 0 - pin was successfully deregistered from this dpll device,
+ * * -ENXIO - given pin was not registered with this dpll device,
+ * * -EINVAL - pin pointer is not valid.
+ */
+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin);
+
+/**
+ * dpll_pin_free - free memory allocated for a pin
+ * @pin: pointer to allocated pin object being freed
+ *
+ * Shared pins must be deregistered from all dpll devices before freeing them,
+ * otherwise the memory won't be freed.
+ */
+void dpll_pin_free(struct dpll_pin *pin);
+
+/**
+ * dpll_muxed_pin_register - register a pin to a muxed-type pin
+ * @parent_pin_description: parent pin description as given on it's allocation
+ * @pin: pointer to allocated pin object being registered with a parent pin
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops*
+ *
+ * In case of multiplexed pins, allows registring them under a single
+ * parent pin.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this parent pin,
+ * * -EBUSY - couldn't assign id for a pin.
+ */
+int dpll_muxed_pin_register(struct dpll_device *dpll,
+			    const char *parent_pin_description,
+			    struct dpll_pin *pin,
+			    struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_device_get_by_clock_id - find a dpll by its clock_id, type and index
+ * @clock_id: clock_id of dpll, as given by driver on ``dpll_device_alloc``
+ * @type: type of dpll, as given by driver on ``dpll_device_alloc``
+ * @idx: index of dpll, as given by driver on ``dpll_device_alloc``
+ *
+ * Allows multiple driver instances using one physical DPLL to find
+ * and share already registered DPLL device.
+ *
+ * Return: pointer if device was found, NULL otherwise.
+ */
+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
+						enum dpll_type type, u8 idx);
+
+/**
+ * dpll_device_notify - notify on dpll device change
+ * @dpll: dpll device pointer
+ * @event: type of change
+ *
+ * Broadcast event to the netlink multicast registered listeners.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change event);
+
+/**
+ * dpll_pin_notify - notify on dpll pin change
+ * @dpll: dpll device pointer
+ * @pin: dpll pin pointer
+ * @event: type of change
+ *
+ * Broadcast event to the netlink multicast registered listeners.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dpll_event_change event);
+
+#endif
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
new file mode 100644
index 000000000000..b7dbdd814b5c
--- /dev/null
+++ b/include/uapi/linux/dpll.h
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_DPLL_H
+#define _UAPI_LINUX_DPLL_H
+
+#define DPLL_NAME_LEN		32
+#define DPLL_DESC_LEN		20
+#define DPLL_PIN_DESC_LEN	20
+
+/* Adding event notification support elements */
+#define DPLL_FAMILY_NAME	"dpll"
+#define DPLL_VERSION		0x01
+#define DPLL_MONITOR_GROUP_NAME	"monitor"
+
+#define DPLL_FILTER_PINS	1
+#define DPLL_FILTER_STATUS	2
+
+/* dplla - Attributes of dpll generic netlink family
+ *
+ * @DPLLA_UNSPEC - invalid attribute
+ * @DPLLA_ID - ID of a dpll device (unsigned int)
+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH size)
+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum dpll_mode)
+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll
+ *	(unsigned int)
+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)
+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum dpll_clock_class)
+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests (int,
+ *	sum of DPLL_DUMP_FILTER_* defines)
+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by driver
+ *	(char array of PIN_DESC_LEN size)
+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
+ *	(enum dpll_pin_signal_type)
+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
+ *	(enum dpll_pin_signal_type)
+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
+ *	(unsigned int)
+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
+ *	(enum dpll_pin_mode)
+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
+ * @DPLLA_CHANGE_TYPE - type of device change event
+ *	(enum dpll_change_type)
+ **/
+enum dplla {
+	DPLLA_UNSPEC,
+	DPLLA_ID,
+	DPLLA_NAME,
+	DPLLA_MODE,
+	DPLLA_MODE_SUPPORTED,
+	DPLLA_SOURCE_PIN_IDX,
+	DPLLA_LOCK_STATUS,
+	DPLLA_TEMP,
+	DPLLA_CLOCK_ID,
+	DPLLA_CLOCK_CLASS,
+	DPLLA_FILTER,
+	DPLLA_PIN,
+	DPLLA_PIN_IDX,
+	DPLLA_PIN_DESCRIPTION,
+	DPLLA_PIN_TYPE,
+	DPLLA_PIN_SIGNAL_TYPE,
+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
+	DPLLA_PIN_CUSTOM_FREQ,
+	DPLLA_PIN_MODE,
+	DPLLA_PIN_MODE_SUPPORTED,
+	DPLLA_PIN_PRIO,
+	DPLLA_PIN_PARENT_IDX,
+	DPLLA_PIN_NETIFINDEX,
+	DPLLA_CHANGE_TYPE,
+	__DPLLA_MAX,
+};
+
+#define DPLLA_MAX (__DPLLA_MAX - 1)
+
+/* dpll_lock_status - DPLL status provides information of device status
+ *
+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid (or is in
+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid signal
+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a valid lock
+ *	or was forced by DPLL_MODE_HOLDOVER mode)
+ **/
+enum dpll_lock_status {
+	DPLL_LOCK_STATUS_UNSPEC,
+	DPLL_LOCK_STATUS_UNLOCKED,
+	DPLL_LOCK_STATUS_CALIBRATING,
+	DPLL_LOCK_STATUS_LOCKED,
+	DPLL_LOCK_STATUS_HOLDOVER,
+
+	__DPLL_LOCK_STATUS_MAX,
+};
+
+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
+
+/* dpll_pin_type - signal types
+ *
+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
+ * @DPLL_PIN_TYPE_EXT - external source
+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
+ **/
+enum dpll_pin_type {
+	DPLL_PIN_TYPE_UNSPEC,
+	DPLL_PIN_TYPE_MUX,
+	DPLL_PIN_TYPE_EXT,
+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+	DPLL_PIN_TYPE_INT_OSCILLATOR,
+	DPLL_PIN_TYPE_GNSS,
+
+	__DPLL_PIN_TYPE_MAX,
+};
+
+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
+
+/* dpll_pin_signal_type - signal types
+ *
+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value defined
+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
+ **/
+enum dpll_pin_signal_type {
+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
+
+	__DPLL_PIN_SIGNAL_TYPE_MAX,
+};
+
+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
+
+/* dpll_pin_mode - available pin states
+ *
+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
+ * @DPLL_PIN_MODE_CONNECTED - pin connected
+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
+ **/
+enum dpll_pin_mode {
+	DPLL_PIN_MODE_UNSPEC,
+	DPLL_PIN_MODE_CONNECTED,
+	DPLL_PIN_MODE_DISCONNECTED,
+	DPLL_PIN_MODE_SOURCE,
+	DPLL_PIN_MODE_OUTPUT,
+
+	__DPLL_PIN_MODE_MAX,
+};
+
+#define DPLL_PIN_MODE_MAX (__DPLL_PIN_MODE_MAX - 1)
+
+/**
+ * dpll_event - Events of dpll generic netlink family
+ *
+ * @DPLL_EVENT_UNSPEC - invalid event type
+ * @DPLL_EVENT_DEVICE_CREATE - dpll device created
+ * @DPLL_EVENT_DEVICE_DELETE - dpll device deleted
+ * @DPLL_EVENT_DEVICE_CHANGE - attribute of dpll device or pin changed
+ **/
+enum dpll_event {
+	DPLL_EVENT_UNSPEC,
+	DPLL_EVENT_DEVICE_CREATE,
+	DPLL_EVENT_DEVICE_DELETE,
+	DPLL_EVENT_DEVICE_CHANGE,
+
+	__DPLL_EVENT_MAX,
+};
+
+#define DPLL_EVENT_MAX (__DPLL_EVENT_MAX - 1)
+
+/**
+ * dpll_change_type - values of events in case of device change event
+ * (DPLL_EVENT_DEVICE_CHANGE)
+ *
+ * @DPLL_CHANGE_UNSPEC - invalid event type
+ * @DPLL_CHANGE_MODE - mode changed
+ * @DPLL_CHANGE_LOCK_STATUS - lock status changed
+ * @DPLL_CHANGE_SOURCE_PIN - source pin changed,
+ * @DPLL_CHANGE_TEMP - temperature changed
+ * @DPLL_CHANGE_PIN_ADD - source pin added,
+ * @DPLL_CHANGE_PIN_DEL - source pin deleted,
+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
+ * @DPLL_CHANGE_PIN_MODE - pin state changed
+ * @DPLL_CHANGE_PIN_PRIO - pin prio changed
+ **/
+enum dpll_event_change {
+	DPLL_CHANGE_UNSPEC,
+	DPLL_CHANGE_MODE,
+	DPLL_CHANGE_LOCK_STATUS,
+	DPLL_CHANGE_SOURCE_PIN,
+	DPLL_CHANGE_TEMP,
+	DPLL_CHANGE_PIN_ADD,
+	DPLL_CHANGE_PIN_DEL,
+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
+	DPLL_CHANGE_PIN_MODE,
+	DPLL_CHANGE_PIN_PRIO,
+
+	__DPLL_CHANGE_MAX,
+};
+
+#define DPLL_CHANGE_MAX (__DPLL_CHANGE_MAX - 1)
+
+/**
+ * dpll_cmd - Commands supported by the dpll generic netlink family
+ *
+ * @DPLL_CMD_UNSPEC - invalid message type
+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes of
+ *	single dpll device and it's pins
+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
+ **/
+enum dpll_cmd {
+	DPLL_CMD_UNSPEC,
+	DPLL_CMD_DEVICE_GET,
+	DPLL_CMD_DEVICE_SET,
+	DPLL_CMD_PIN_SET,
+
+	__DPLL_CMD_MAX,
+};
+
+#define DPLL_CMD_MAX (__DPLL_CMD_MAX - 1)
+
+/**
+ * dpll_mode - Working-modes a dpll can support. Modes differentiate how
+ * dpll selects one of its sources to syntonize with a source.
+ *
+ * @DPLL_MODE_UNSPEC - invalid
+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request to dpll
+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by dpll
+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
+ **/
+enum dpll_mode {
+	DPLL_MODE_UNSPEC,
+	DPLL_MODE_MANUAL,
+	DPLL_MODE_AUTOMATIC,
+	DPLL_MODE_HOLDOVER,
+	DPLL_MODE_FREERUN,
+	DPLL_MODE_NCO,
+
+	__DPLL_MODE_MAX,
+};
+
+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
+
+/**
+ * dpll_clock_class - enumerate quality class of a DPLL clock as specified in
+ * Recommendation ITU-T G.8273.2/Y.1368.2.
+ */
+enum dpll_clock_class {
+	DPLL_CLOCK_CLASS_UNSPEC,
+	DPLL_CLOCK_CLASS_A,
+	DPLL_CLOCK_CLASS_B,
+	DPLL_CLOCK_CLASS_C,
+
+	__DPLL_CLOCK_CLASS_MAX,
+};
+
+#define DPLL_CLOCK_CLASS_MAX (__DPLL_CLOCK_CLASS_MAX - 1)
+
+/**
+ * enum dpll_type - type of dpll, integer value of enum is embedded into
+ * name of DPLL device (DPLLA_NAME)
+ *
+ * @DPLL_TYPE_UNSPEC - unspecified
+ * @DPLL_TYPE_PPS - dpll produces Pulse-Per-Second signal
+ * @DPLL_TYPE_EEC - dpll drives the Ethernet Equipment Clock
+ */
+enum dpll_type {
+	DPLL_TYPE_UNSPEC,
+	DPLL_TYPE_PPS,
+	DPLL_TYPE_EEC,
+
+	__DPLL_TYPE_MAX
+};
+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
+
+#endif /* _UAPI_LINUX_DPLL_H */
-- 
2.30.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-01-17 18:00   ` Vadim Fedorenko
  0 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, netdev, linux-arm-kernel, linux-clk,
	Milena Olech, Michal Michalik

DPLL framework is used to represent and configure DPLL devices
in systems. Each device that has DPLL and can configure sources
and outputs can use this framework. Netlink interface is used to
provide configuration data and to receive notification messages
about changes in the configuration or status of DPLL device.
Inputs and outputs of the DPLL device are represented as special
objects which could be dynamically added to and removed from DPLL
device.

Co-developed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Co-developed-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
---
 MAINTAINERS                 |    8 +
 drivers/Kconfig             |    2 +
 drivers/Makefile            |    1 +
 drivers/dpll/Kconfig        |    7 +
 drivers/dpll/Makefile       |    9 +
 drivers/dpll/dpll_core.c    | 1010 +++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h    |  105 ++++
 drivers/dpll/dpll_netlink.c |  883 ++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |   24 +
 include/linux/dpll.h        |  282 ++++++++++
 include/uapi/linux/dpll.h   |  294 ++++++++++
 11 files changed, 2625 insertions(+)
 create mode 100644 drivers/dpll/Kconfig
 create mode 100644 drivers/dpll/Makefile
 create mode 100644 drivers/dpll/dpll_core.c
 create mode 100644 drivers/dpll/dpll_core.h
 create mode 100644 drivers/dpll/dpll_netlink.c
 create mode 100644 drivers/dpll/dpll_netlink.h
 create mode 100644 include/linux/dpll.h
 create mode 100644 include/uapi/linux/dpll.h

diff --git a/MAINTAINERS b/MAINTAINERS
index f82dd8d43c2b..de8a10b21ce8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6411,6 +6411,14 @@ F:	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
 F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
 F:	drivers/net/ethernet/freescale/dpaa2/dpsw*
 
+DPLL CLOCK SUBSYSTEM
+M:	Vadim Fedorenko <vadfed@fb.com>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/dpll/*
+F:	include/net/dpll.h
+F:	include/uapi/linux/dpll.h
+
 DRBD DRIVER
 M:	Philipp Reisner <philipp.reisner@linbit.com>
 M:	Lars Ellenberg <lars.ellenberg@linbit.com>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 968bd0a6fd78..453df9e1210d 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
 
 source "drivers/hte/Kconfig"
 
+source "drivers/dpll/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index bdf1c66141c9..7cbee58bc692 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER)		+= counter/
 obj-$(CONFIG_MOST)		+= most/
 obj-$(CONFIG_PECI)		+= peci/
 obj-$(CONFIG_HTE)		+= hte/
+obj-$(CONFIG_DPLL)		+= dpll/
diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
new file mode 100644
index 000000000000..a4cae73f20d3
--- /dev/null
+++ b/drivers/dpll/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Generic DPLL drivers configuration
+#
+
+config DPLL
+  bool
diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
new file mode 100644
index 000000000000..b18cf848a010
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DPLL drivers.
+#
+
+obj-$(CONFIG_DPLL)          += dpll_sys.o
+dpll_sys-y                  += dpll_core.o
+dpll_sys-y                  += dpll_netlink.o
+
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..fec534f17827
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,1010 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  dpll_core.c - Generic DPLL Management class support.
+ *
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dpll_core.h"
+
+/**
+ * struct dpll_pin - structure for a dpll pin
+ * @idx:		unique id number for each pin
+ * @parent_pin:		parent pin
+ * @type:		type of the pin
+ * @ops:		operations this &dpll_pin supports
+ * @priv:		pointer to private information of owner
+ * @ref_dplls:		array of registered dplls
+ * @description:	name to distinguish the pin
+ */
+struct dpll_pin {
+	u32 idx;
+	struct dpll_pin *parent_pin;
+	enum dpll_pin_type type;
+	struct dpll_pin_ops *ops;
+	void *priv;
+	struct xarray ref_dplls;
+	char description[DPLL_PIN_DESC_LEN];
+};
+static DEFINE_MUTEX(dpll_device_xa_lock);
+
+static DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
+#define DPLL_REGISTERED		XA_MARK_1
+#define PIN_REGISTERED		XA_MARK_1
+
+#define ASSERT_DPLL_REGISTERED(d)                                           \
+	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+#define ASSERT_DPLL_NOT_REGISTERED(d)                                      \
+	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+
+struct dpll_pin_ref {
+	struct dpll_device *dpll;
+	struct dpll_pin_ops *ops;
+	void *priv;
+};
+
+/**
+ * dpll_device_get_by_id - find dpll device by it's id
+ * @id: id of searched dpll
+ *
+ * Return: dpll_device struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_device_get_by_id(int id)
+{
+	struct dpll_device *dpll = NULL;
+
+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
+		dpll = xa_load(&dpll_device_xa, id);
+
+	return dpll;
+}
+
+/**
+ * dpll_device_get_by_name - find dpll device by it's id
+ * @name: name of searched dpll
+ *
+ * Return: dpll_device struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_device_get_by_name(const char *name)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
+		if (!strcmp(dev_name(&dpll->dev), name)) {
+			ret = dpll;
+			break;
+		}
+	}
+	mutex_unlock(&dpll_device_xa_lock);
+
+	return ret;
+}
+
+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
+						enum dpll_type type, u8 idx)
+{
+	struct dpll_device *dpll, *ret = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
+		if (dpll->clock_id == clock_id) {
+			if (dpll->type == type) {
+				if (dpll->dev_driver_idx == idx) {
+					ret = dpll;
+					break;
+				}
+			}
+		}
+	}
+	mutex_unlock(&dpll_device_xa_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
+
+static void dpll_device_release(struct device *dev)
+{
+	struct dpll_device *dpll;
+
+	mutex_lock(&dpll_device_xa_lock);
+	dpll = to_dpll_device(dev);
+	dpll_device_unregister(dpll);
+	mutex_unlock(&dpll_device_xa_lock);
+	dpll_device_free(dpll);
+}
+
+static struct class dpll_class = {
+	.name = "dpll",
+	.dev_release = dpll_device_release,
+};
+
+struct dpll_device
+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
+		   const u64 clock_id, enum dpll_clock_class clock_class,
+		   u8 dev_driver_idx, void *priv, struct device *parent)
+{
+	struct dpll_device *dpll;
+	int ret;
+
+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
+	if (!dpll)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&dpll->lock);
+	dpll->ops = ops;
+	dpll->dev.class = &dpll_class;
+	dpll->parent = parent;
+	dpll->type = type;
+	dpll->dev_driver_idx = dev_driver_idx;
+	dpll->clock_id = clock_id;
+	dpll->clock_class = clock_class;
+
+	mutex_lock(&dpll_device_xa_lock);
+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
+		       xa_limit_16b, GFP_KERNEL);
+	if (ret)
+		goto error;
+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
+		     dev_driver_idx);
+	dpll->priv = priv;
+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_device_xa_lock);
+	dpll_notify_device_create(dpll);
+
+	return dpll;
+
+error:
+	mutex_unlock(&dpll_device_xa_lock);
+	kfree(dpll);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(dpll_device_alloc);
+
+void dpll_device_free(struct dpll_device *dpll)
+{
+	WARN_ON_ONCE(!dpll);
+	WARN_ON_ONCE(!xa_empty(&dpll->pins));
+	xa_destroy(&dpll->pins);
+	mutex_destroy(&dpll->lock);
+	kfree(dpll);
+}
+EXPORT_SYMBOL_GPL(dpll_device_free);
+
+/**
+ * dpll_device_unregister - unregister dpll device
+ * @dpll: registered dpll pointer
+ *
+ * Note: It does not free the memory
+ */
+void dpll_device_unregister(struct dpll_device *dpll)
+{
+	ASSERT_DPLL_REGISTERED(dpll);
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_erase(&dpll_device_xa, dpll->id);
+	dpll_notify_device_delete(dpll);
+	mutex_unlock(&dpll_device_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_unregister);
+
+/**
+ * dpll_id - return dpll id
+ * @dpll: registered dpll pointer
+ *
+ * Return: dpll id.
+ */
+u32 dpll_id(struct dpll_device *dpll)
+{
+	return dpll->id;
+}
+
+/**
+ * dpll_pin_idx - return index of a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ *
+ * Return: index of a pin or PIN_IDX_INVALID if not found.
+ */
+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	struct dpll_pin *pos;
+	unsigned long index;
+
+	xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED) {
+		if (pos == pin)
+			return pin->idx;
+	}
+
+	return PIN_IDX_INVALID;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_idx);
+
+const char *dpll_dev_name(struct dpll_device *dpll)
+{
+	return dev_name(&dpll->dev);
+}
+
+struct dpll_pin *dpll_pin_alloc(const char *description,
+				const enum dpll_pin_type pin_type)
+{
+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
+
+	if (!pin)
+		return ERR_PTR(-ENOMEM);
+	if (pin_type <= DPLL_PIN_TYPE_UNSPEC ||
+	    pin_type > DPLL_PIN_TYPE_MAX)
+		return ERR_PTR(-EINVAL);
+
+	strncpy(pin->description, description, DPLL_PIN_DESC_LEN);
+	pin->description[DPLL_PIN_DESC_LEN - 1] = '\0';
+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
+	pin->type = pin_type;
+
+	return pin;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_alloc);
+
+static int dpll_alloc_pin_on_xa(struct xarray *pins, struct dpll_pin *pin)
+{
+	struct dpll_pin *pos;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(pins, index, pos) {
+		if (pos == pin ||
+		    !strncmp(pos->description, pin->description,
+			     DPLL_PIN_DESC_LEN))
+			return -EEXIST;
+	}
+
+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
+	if (!ret)
+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);
+
+	return ret;
+}
+
+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device *dpll,
+			    struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin_ref *ref, *pos;
+	unsigned long index;
+	u32 idx;
+
+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
+	if (!ref)
+		return -ENOMEM;
+	ref->dpll = dpll;
+	ref->ops = ops;
+	ref->priv = priv;
+	if (!xa_empty(&pin->ref_dplls)) {
+		xa_for_each(&pin->ref_dplls, index, pos) {
+			if (pos->dpll == ref->dpll)
+				return -EEXIST;
+		}
+	}
+
+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
+}
+
+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device *dpll)
+{
+	struct dpll_pin_ref *pos;
+	unsigned long index;
+
+	xa_for_each(&pin->ref_dplls, index, pos) {
+		if (pos->dpll == dpll) {
+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
+			break;
+		}
+	}
+}
+
+static int pin_deregister_from_xa(struct xarray *xa_pins, struct dpll_pin *pin)
+{
+	struct dpll_pin *pos;
+	unsigned long index;
+
+	xa_for_each(xa_pins, index, pos) {
+		if (pos == pin) {
+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));
+			return 0;
+		}
+	}
+
+	return -ENXIO;
+}
+
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		      struct dpll_pin_ops *ops, void *priv)
+{
+	int ret;
+
+	if (!pin || !ops)
+		return -EINVAL;
+
+	mutex_lock(&dpll->lock);
+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
+	if (!ret) {
+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
+		if (ret)
+			pin_deregister_from_xa(&dpll->pins, pin);
+	}
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_register);
+
+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins, int idx)
+{
+	struct dpll_pin *pos;
+	unsigned long index;
+
+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
+		if (pos->idx == idx)
+			return pos;
+	}
+
+	return NULL;
+}
+
+/**
+ * dpll_pin_get_by_idx - find a pin by its index
+ * @dpll: dpll device pointer
+ * @idx: index of pin
+ *
+ * Allows multiple driver instances using one physical DPLL to find
+ * and share pin already registered with existing dpll device.
+ *
+ * Return: pointer if pin was found, NULL otherwise.
+ */
+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
+{
+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
+}
+
+	struct dpll_pin
+*dpll_pin_get_by_description(struct dpll_device *dpll, const char *description)
+{
+	struct dpll_pin *pos, *pin = NULL;
+	unsigned long index;
+
+	xa_for_each(&dpll->pins, index, pos) {
+		if (!strncmp(pos->description, description,
+			     DPLL_PIN_DESC_LEN)) {
+			pin = pos;
+			break;
+		}
+	}
+
+	return pin;
+}
+
+int
+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
+			 struct dpll_device *dpll,
+			 const char *shared_pin_description,
+			 struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin *pin;
+	int ret;
+
+	mutex_lock(&dpll_pin_owner->lock);
+	pin = dpll_pin_get_by_description(dpll_pin_owner,
+					  shared_pin_description);
+	if (!pin) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+	ret = dpll_pin_register(dpll, pin, ops, priv);
+unlock:
+	mutex_unlock(&dpll_pin_owner->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);
+
+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	int ret = 0;
+
+	if (xa_empty(&dpll->pins))
+		return -ENOENT;
+
+	mutex_lock(&dpll->lock);
+	ret = pin_deregister_from_xa(&dpll->pins, pin);
+	if (!ret)
+		dpll_pin_ref_del(pin, dpll);
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_DEL);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_deregister);
+
+void dpll_pin_free(struct dpll_pin *pin)
+{
+	if (!xa_empty(&pin->ref_dplls))
+		return;
+
+	xa_destroy(&pin->ref_dplls);
+	kfree(pin);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_free);
+
+int dpll_muxed_pin_register(struct dpll_device *dpll,
+			    const char *parent_pin_description,
+			    struct dpll_pin *pin,
+			    struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin *parent_pin;
+	int ret;
+
+	if (!parent_pin_description || !pin)
+		return -EINVAL;
+
+	mutex_lock(&dpll->lock);
+	parent_pin = dpll_pin_get_by_description(dpll, parent_pin_description);
+	if (!parent_pin)
+		return -EINVAL;
+	if (parent_pin->type != DPLL_PIN_TYPE_MUX)
+		return -EPERM;
+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
+	if (!ret)
+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
+	if (!ret)
+		pin->parent_pin = parent_pin;
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
+
+/**
+ * dpll_pin_first - get first registered pin
+ * @dpll: registered dpll pointer
+ * @index: found pin index (out)
+ *
+ * Return: dpll_pin struct if found, NULL otherwise.
+ */
+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long *index)
+{
+	*index = 0;
+
+	return xa_find(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
+}
+
+/**
+ * dpll_pin_next - get next registered pin to the relative pin
+ * @dpll: registered dpll pointer
+ * @index: relative pin index (in and out)
+ *
+ * Return: dpll_pin struct if found, NULL otherwise.
+ */
+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index)
+{
+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
+}
+
+/**
+ * dpll_first - get first registered dpll device
+ * @index: found dpll index (out)
+ *
+ * Return: dpll_device struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_first(unsigned long *index)
+{
+	*index = 0;
+
+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
+}
+
+/**
+ * dpll_pin_next - get next registered dpll device to the relative pin
+ * @index: relative dpll index (in and out)
+ *
+ * Return: dpll_pin struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_next(unsigned long *index)
+{
+	return xa_find_after(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
+}
+
+static struct dpll_pin_ref
+*dpll_pin_find_ref(const struct dpll_device *dpll, const struct dpll_pin *pin)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long index;
+
+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
+		if (ref->dpll != dpll)
+			continue;
+		else
+			return ref;
+	}
+
+	return NULL;
+}
+
+/**
+ * dpll_pin_type_get - get type of a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @type: on success - configured pin type
+ *
+ * Return:
+ * * 0 - successfully got pin's type
+ * * negative - failed to get pin's type
+ */
+int dpll_pin_type_get(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin,
+		      enum dpll_pin_type *type)
+{
+	if (!pin)
+		return -ENODEV;
+	*type = pin->type;
+
+	return 0;
+}
+
+/**
+ * dpll_pin_signal_type_get - get signal type of a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @type: on success - configured signal type
+ *
+ * Return:
+ * * 0 - successfully got signal type
+ * * negative - failed to obtain signal type
+ */
+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     enum dpll_pin_signal_type *type)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->signal_type_get)
+		return -EOPNOTSUPP;
+	ret = ref->ops->signal_type_get(ref->dpll, pin, type);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_signal_type_set - set signal type of a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @type: type to be set
+ *
+ * Return:
+ * * 0 - signal type set
+ * * negative - failed to set signal type
+ */
+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     const enum dpll_pin_signal_type type)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long index;
+	int ret;
+
+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
+		if (!ref->dpll)
+			return -EFAULT;
+		if (!ref || !ref->ops || !ref->ops->signal_type_set)
+			return -EOPNOTSUPP;
+		if (ref->dpll != dpll)
+			mutex_lock(&ref->dpll->lock);
+		ret = ref->ops->signal_type_set(ref->dpll, pin, type);
+		if (ref->dpll != dpll)
+			mutex_unlock(&ref->dpll->lock);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * dpll_pin_signal_type_supported - check if signal type is supported on a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @type: type being checked
+ * @supported: on success - if given signal type is supported
+ *
+ * Return:
+ * * 0 - successfully got supported signal type
+ * * negative - failed to obtain supported signal type
+ */
+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
+				   const struct dpll_pin *pin,
+				   const enum dpll_pin_signal_type type,
+				   bool *supported)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->signal_type_supported)
+		return -EOPNOTSUPP;
+	*supported = ref->ops->signal_type_supported(ref->dpll, pin, type);
+
+	return 0;
+}
+
+/**
+ * dpll_pin_mode_active - check if given mode is active on a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @mode: mode being checked
+ * @active: on success - if mode is active
+ *
+ * Return:
+ * * 0 - successfully checked if mode is active
+ * * negative - failed to check for active mode
+ */
+int dpll_pin_mode_active(const struct dpll_device *dpll,
+			  const struct dpll_pin *pin,
+			  const enum dpll_pin_mode mode,
+			  bool *active)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->mode_active)
+		return -EOPNOTSUPP;
+	*active = ref->ops->mode_active(ref->dpll, pin, mode);
+
+	return 0;
+}
+
+/**
+ * dpll_pin_mode_supported - check if given mode is supported on a pin
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @mode: mode being checked
+ * @supported: on success - if mode is supported
+ *
+ * Return:
+ * * 0 - successfully checked if mode is supported
+ * * negative - failed to check for supported mode
+ */
+int dpll_pin_mode_supported(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     const enum dpll_pin_mode mode,
+			     bool *supported)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->mode_supported)
+		return -EOPNOTSUPP;
+	*supported = ref->ops->mode_supported(ref->dpll, pin, mode);
+
+	return 0;
+}
+
+/**
+ * dpll_pin_mode_set - set pin's mode
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @mode: mode being set
+ *
+ * Return:
+ * * 0 - successfully set the mode
+ * * negative - failed to set the mode
+ */
+int dpll_pin_mode_set(const struct dpll_device *dpll,
+		       const struct dpll_pin *pin,
+		       const enum dpll_pin_mode mode)
+{
+	struct dpll_pin_ref *ref;
+	unsigned long index;
+	int ret;
+
+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
+		if (!ref)
+			return -ENODEV;
+		if (!ref->ops || !ref->ops->mode_enable)
+			return -EOPNOTSUPP;
+		if (ref->dpll != dpll)
+			mutex_lock(&ref->dpll->lock);
+		ret = ref->ops->mode_enable(ref->dpll, pin, mode);
+		if (ref->dpll != dpll)
+			mutex_unlock(&ref->dpll->lock);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * dpll_pin_custom_freq_get - get pin's custom frequency
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @freq: on success - custom frequency of a pin
+ *
+ * Return:
+ * * 0 - successfully got custom frequency
+ * * negative - failed to obtain custom frequency
+ */
+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin, u32 *freq)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->custom_freq_get)
+		return -EOPNOTSUPP;
+	ret = ref->ops->custom_freq_get(ref->dpll, pin, freq);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_custom_freq_set - set pin's custom frequency
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @freq: custom frequency to be set
+ *
+ * Return:
+ * * 0 - successfully set custom frequency
+ * * negative - failed to set custom frequency
+ */
+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin, const u32 freq)
+{
+	enum dpll_pin_signal_type type;
+	struct dpll_pin_ref *ref;
+	unsigned long index;
+	int ret;
+
+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
+		if (!ref)
+			return -ENODEV;
+		if (!ref->ops || !ref->ops->custom_freq_set ||
+		    !ref->ops->signal_type_get)
+			return -EOPNOTSUPP;
+		if (dpll != ref->dpll)
+			mutex_lock(&ref->dpll->lock);
+		ret = ref->ops->signal_type_get(dpll, pin, &type);
+		if (!ret && type == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ)
+			ret = ref->ops->custom_freq_set(ref->dpll, pin, freq);
+		if (dpll != ref->dpll)
+			mutex_unlock(&ref->dpll->lock);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * dpll_pin_prio_get - get pin's prio on dpll
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @prio: on success - priority of a pin on a dpll
+ *
+ * Return:
+ * * 0 - successfully got priority
+ * * negative - failed to obtain priority
+ */
+int dpll_pin_prio_get(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin, u32 *prio)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->prio_get)
+		return -EOPNOTSUPP;
+	ret = ref->ops->prio_get(ref->dpll, pin, prio);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_prio_set - set pin's prio on dpll
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @prio: priority of a pin to be set on a dpll
+ *
+ * Return:
+ * * 0 - successfully set priority
+ * * negative - failed to set the priority
+ */
+int dpll_pin_prio_set(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin, const u32 prio)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->prio_set)
+		return -EOPNOTSUPP;
+	ret = ref->ops->prio_set(ref->dpll, pin, prio);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_netifindex_get - get pin's netdev iterface index
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @netifindex: on success - index of a netdevice associated with pin
+ *
+ * Return:
+ * * 0 - successfully got netdev interface index
+ * * negative - failed to obtain netdev interface index
+ */
+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin,
+			    int *netifindex)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->net_if_idx_get)
+		return -EOPNOTSUPP;
+	ret = ref->ops->net_if_idx_get(ref->dpll, pin, netifindex);
+
+	return ret;
+}
+
+/**
+ * dpll_pin_description - provide pin's description string
+ * @pin: registered pin pointer
+ *
+ * Return: pointer to a description string.
+ */
+const char *dpll_pin_description(struct dpll_pin *pin)
+{
+	return pin->description;
+}
+
+/**
+ * dpll_pin_parent - provide pin's parent pin if available
+ * @pin: registered pin pointer
+ *
+ * Return: pointer to aparent if found, NULL otherwise.
+ */
+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)
+{
+	return pin->parent_pin;
+}
+
+/**
+ * dpll_mode_set - handler for dpll mode set
+ * @dpll: registered dpll pointer
+ * @mode: mode to be set
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode)
+{
+	int ret;
+
+	if (!dpll->ops || !dpll->ops)
+		return -EOPNOTSUPP;
+
+	ret = dpll->ops->mode_set(dpll, mode);
+
+	return ret;
+}
+
+/**
+ * dpll_source_idx_set - handler for selecting a dpll's source
+ * @dpll: registered dpll pointer
+ * @source_pin_idx: index of a source pin to e selected
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_source_idx_set(struct dpll_device *dpll, const u32 source_pin_idx)
+{
+	struct dpll_pin_ref *ref;
+	struct dpll_pin *pin;
+	int ret;
+
+	pin = dpll_pin_get_by_idx_from_xa(&dpll->pins, source_pin_idx);
+	if (!pin)
+		return -ENXIO;
+	ref = dpll_pin_find_ref(dpll, pin);
+	if (!ref || !ref->ops)
+		return -EFAULT;
+	if (!ref->ops->select)
+		return -ENODEV;
+	ret = ref->ops->select(ref->dpll, pin);
+
+	return ret;
+}
+
+/**
+ * dpll_lock - locks the dpll using internal mutex
+ * @dpll: registered dpll pointer
+ */
+void dpll_lock(struct dpll_device *dpll)
+{
+	mutex_lock(&dpll->lock);
+}
+
+/**
+ * dpll_unlock - unlocks the dpll using internal mutex
+ * @dpll: registered dpll pointer
+ */
+void dpll_unlock(struct dpll_device *dpll)
+{
+	mutex_unlock(&dpll->lock);
+}
+
+enum dpll_pin_type dpll_pin_type(const struct dpll_pin *pin)
+{
+	return pin->type;
+}
+
+void *dpll_priv(const struct dpll_device *dpll)
+{
+	return dpll->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_priv);
+
+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin *pin)
+{
+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
+
+	if (!ref)
+		return NULL;
+
+	return ref->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_priv);
+
+static int __init dpll_init(void)
+{
+	int ret;
+
+	ret = dpll_netlink_init();
+	if (ret)
+		goto error;
+
+	ret = class_register(&dpll_class);
+	if (ret)
+		goto unregister_netlink;
+
+	return 0;
+
+unregister_netlink:
+	dpll_netlink_finish();
+error:
+	mutex_destroy(&dpll_device_xa_lock);
+	return ret;
+}
+subsys_initcall(dpll_init);
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
new file mode 100644
index 000000000000..b933d63b60c1
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+#ifndef __DPLL_CORE_H__
+#define __DPLL_CORE_H__
+
+#include <linux/dpll.h>
+
+#include "dpll_netlink.h"
+
+#define to_dpll_device(_dev) \
+	container_of(_dev, struct dpll_device, dev)
+
+/**
+ * struct dpll_device - structure for a DPLL device
+ * @id:		unique id number for each device
+ * @dev:	struct device for this dpll device
+ * @parent:	parent device
+ * @ops:	operations this &dpll_device supports
+ * @lock:	mutex to serialize operations
+ * @type:	type of a dpll
+ * @priv:	pointer to private information of owner
+ * @pins:	list of pointers to pins registered with this dpll
+ * @clock_id:	unique identifier (clock_id) of a dpll
+ * @clock_class	quality class of a DPLL clock
+ * @dev_driver_idx: provided by driver for
+ */
+struct dpll_device {
+	u32 id;
+	struct device dev;
+	struct device *parent;
+	struct dpll_device_ops *ops;
+	struct mutex lock;
+	enum dpll_type type;
+	void *priv;
+	struct xarray pins;
+	u64 clock_id;
+	enum dpll_clock_class clock_class;
+	u8 dev_driver_idx;
+};
+
+#define for_each_pin_on_dpll(dpll, pin, i)			\
+	for (pin = dpll_pin_first(dpll, &i); pin != NULL;	\
+	     pin = dpll_pin_next(dpll, &i))
+
+#define for_each_dpll(dpll, i)                         \
+	for (dpll = dpll_first(&i); dpll != NULL; dpll = dpll_next(&i))
+
+struct dpll_device *dpll_device_get_by_id(int id);
+
+struct dpll_device *dpll_device_get_by_name(const char *name);
+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long *index);
+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index);
+struct dpll_device *dpll_first(unsigned long *index);
+struct dpll_device *dpll_next(unsigned long *index);
+void dpll_device_unregister(struct dpll_device *dpll);
+u32 dpll_id(struct dpll_device *dpll);
+const char *dpll_dev_name(struct dpll_device *dpll);
+void dpll_lock(struct dpll_device *dpll);
+void dpll_unlock(struct dpll_device *dpll);
+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
+int dpll_pin_type_get(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin,
+		      enum dpll_pin_type *type);
+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     enum dpll_pin_signal_type *type);
+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     const enum dpll_pin_signal_type type);
+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
+				   const struct dpll_pin *pin,
+				   const enum dpll_pin_signal_type type,
+				   bool *supported);
+int dpll_pin_mode_active(const struct dpll_device *dpll,
+			 const struct dpll_pin *pin,
+			 const enum dpll_pin_mode mode,
+			 bool *active);
+int dpll_pin_mode_supported(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin,
+			    const enum dpll_pin_mode mode,
+			    bool *supported);
+int dpll_pin_mode_set(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin,
+		      const enum dpll_pin_mode mode);
+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin, u32 *freq);
+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin, const u32 freq);
+int dpll_pin_prio_get(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin, u32 *prio);
+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx);
+int dpll_pin_prio_set(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin, const u32 prio);
+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin,
+			    int *netifindex);
+const char *dpll_pin_description(struct dpll_pin *pin);
+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin);
+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode);
+int dpll_source_idx_set(struct dpll_device *dpll, const u32 source_pin_idx);
+
+#endif
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..91a1e5025ab2
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,883 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic netlink for DPLL management framework
+ *
+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include "dpll_core.h"
+
+#include <uapi/linux/dpll.h>
+
+static const struct genl_multicast_group dpll_mcgrps[] = {
+	{ .name = DPLL_MONITOR_GROUP_NAME,  },
+};
+
+static const struct nla_policy dpll_cmd_device_get_policy[] = {
+	[DPLLA_ID]		= { .type = NLA_U32 },
+	[DPLLA_NAME]		= { .type = NLA_STRING,
+				    .len = DPLL_NAME_LEN },
+	[DPLLA_FILTER]		= { .type = NLA_U32 },
+};
+
+static const struct nla_policy dpll_cmd_device_set_policy[] = {
+	[DPLLA_ID]		= { .type = NLA_U32 },
+	[DPLLA_NAME]		= { .type = NLA_STRING,
+				    .len = DPLL_NAME_LEN },
+	[DPLLA_MODE]		= { .type = NLA_U32 },
+	[DPLLA_SOURCE_PIN_IDX]	= { .type = NLA_U32 },
+};
+
+static const struct nla_policy dpll_cmd_pin_set_policy[] = {
+	[DPLLA_ID]		= { .type = NLA_U32 },
+	[DPLLA_PIN_IDX]		= { .type = NLA_U32 },
+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
+	[DPLLA_PIN_MODE]	= { .type = NLA_U32 },
+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
+};
+
+struct dpll_dump_ctx {
+	int dump_filter;
+};
+
+static struct genl_family dpll_gnl_family;
+
+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
+{
+	return (struct dpll_dump_ctx *)cb->ctx;
+}
+
+static int dpll_msg_add_id(struct sk_buff *msg, u32 id)
+{
+	if (nla_put_u32(msg, DPLLA_ID, id))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_name(struct sk_buff *msg, const char *name)
+{
+	if (nla_put_string(msg, DPLLA_NAME, name))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int __dpll_msg_add_mode(struct sk_buff *msg, enum dplla msg_type,
+			       enum dpll_mode mode)
+{
+	if (nla_put_s32(msg, msg_type, mode))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll)
+{
+	enum dpll_mode m;
+	int ret;
+
+	if (!dpll->ops->mode_get)
+		return 0;
+	ret = dpll->ops->mode_get(dpll, &m);
+	if (ret)
+		return ret;
+
+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
+}
+
+static int
+dpll_msg_add_modes_supported(struct sk_buff *msg,
+			     const struct dpll_device *dpll)
+{
+	enum dpll_mode i;
+	int ret = 0;
+
+	if (!dpll->ops->mode_supported)
+		return ret;
+
+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
+		if (dpll->ops->mode_supported(dpll, i)) {
+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int dpll_msg_add_clock_id(struct sk_buff *msg,
+				 const struct dpll_device *dpll)
+{
+	if (nla_put_64bit(msg, DPLLA_CLOCK_ID, sizeof(dpll->clock_id),
+			  &dpll->clock_id, 0))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_clock_class(struct sk_buff *msg,
+				    const struct dpll_device *dpll)
+{
+	if (nla_put_s32(msg, DPLLA_CLOCK_CLASS, dpll->clock_class))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	u32 source_idx;
+	int ret;
+
+	if (!dpll->ops->source_pin_idx_get)
+		return 0;
+	ret = dpll->ops->source_pin_idx_get(dpll, &source_idx);
+	if (ret)
+		return ret;
+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	enum dpll_lock_status s;
+	int ret;
+
+	if (!dpll->ops->lock_status_get)
+		return 0;
+	ret = dpll->ops->lock_status_get(dpll, &s);
+	if (ret)
+		return ret;
+	if (nla_put_s32(msg, DPLLA_LOCK_STATUS, s))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	s32 temp;
+	int ret;
+
+	if (!dpll->ops->temp_get)
+		return 0;
+	ret = dpll->ops->temp_get(dpll, &temp);
+	if (ret)
+		return ret;
+	if (nla_put_u32(msg, DPLLA_TEMP, temp))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_idx(struct sk_buff *msg, u32 pin_idx)
+{
+	if (nla_put_u32(msg, DPLLA_PIN_IDX, pin_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_description(struct sk_buff *msg,
+					const char *description)
+{
+	if (nla_put_string(msg, DPLLA_PIN_DESCRIPTION, description))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_parent_idx(struct sk_buff *msg, u32 parent_idx)
+{
+	if (nla_put_u32(msg, DPLLA_PIN_PARENT_IDX, parent_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_device *dpll,
+		      const struct dpll_pin *pin)
+{
+	enum dpll_pin_type t;
+
+	if (dpll_pin_type_get(dpll, pin, &t))
+		return 0;
+
+	if (nla_put_s32(msg, DPLLA_PIN_TYPE, t))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int __dpll_msg_add_pin_signal_type(struct sk_buff *msg,
+					  enum dplla attr,
+					  enum dpll_pin_signal_type type)
+{
+	if (nla_put_s32(msg, attr, type))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_signal_type(struct sk_buff *msg,
+					const struct dpll_device *dpll,
+					const struct dpll_pin *pin)
+{
+	enum dpll_pin_signal_type t;
+	int ret;
+
+	if (dpll_pin_signal_type_get(dpll, pin, &t))
+		return 0;
+	ret = __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
+	if (ret)
+		return ret;
+
+	if (t == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) {
+		u32 freq;
+
+		if (dpll_pin_custom_freq_get(dpll, pin, &freq))
+			return 0;
+		if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
+			return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
+					const struct dpll_device *dpll,
+					const struct dpll_pin *pin)
+{
+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
+	enum dpll_pin_signal_type i;
+	bool supported;
+
+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
+		if (dpll_pin_signal_type_supported(dpll, pin, i, &supported))
+			continue;
+		if (supported) {
+			int ret = __dpll_msg_add_pin_signal_type(msg, da, i);
+
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
+				   const struct dpll_device *dpll,
+				   const struct dpll_pin *pin)
+{
+	enum dpll_pin_mode i;
+	bool active;
+
+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
+		if (dpll_pin_mode_active(dpll, pin, i, &active))
+			return 0;
+		if (active)
+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
+				return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_modes_supported(struct sk_buff *msg,
+					     const struct dpll_device *dpll,
+					     const struct dpll_pin *pin)
+{
+	enum dpll_pin_mode i;
+	bool supported;
+
+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
+		if (dpll_pin_mode_supported(dpll, pin, i, &supported))
+			return 0;
+		if (supported)
+			if (nla_put_s32(msg, DPLLA_PIN_MODE_SUPPORTED, i))
+				return -EMSGSIZE;
+	}
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_device *dpll,
+		      const struct dpll_pin *pin)
+{
+	u32 prio;
+
+	if (dpll_pin_prio_get(dpll, pin, &prio))
+		return 0;
+	if (nla_put_u32(msg, DPLLA_PIN_PRIO, prio))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct dpll_device *dpll,
+			    const struct dpll_pin *pin)
+{
+	int netifindex;
+
+	if (dpll_pin_netifindex_get(dpll, pin, &netifindex))
+		return 0;
+	if (nla_put_s32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+__dpll_cmd_device_dump_one(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
+
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
+
+	return ret;
+}
+
+static int
+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_device *dpll,
+			struct dpll_pin *pin)
+{
+	struct dpll_pin *parent = NULL;
+	int ret;
+
+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_description(msg, dpll_pin_description(pin));
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_type(msg, dpll, pin);
+	if (ret)
+		return ret;
+	parent = dpll_pin_parent(pin);
+	if (parent) {
+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
+								    parent));
+		if (ret)
+			return ret;
+	}
+	ret = dpll_msg_add_pin_signal_type(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_signal_types_supported(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_modes(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_modes_supported(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_prio(msg, dpll, pin);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_pin_netifindex(msg, dpll, pin);
+
+	return ret;
+}
+
+static int __dpll_cmd_dump_pins(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	struct dpll_pin *pin;
+	struct nlattr *attr;
+	unsigned long i;
+	int ret = 0;
+
+	for_each_pin_on_dpll(dpll, pin, i) {
+		attr = nla_nest_start(msg, DPLLA_PIN);
+		if (!attr) {
+			ret = -EMSGSIZE;
+			goto nest_cancel;
+		}
+		ret = __dpll_cmd_pin_dump_one(msg, dpll, pin);
+		if (ret)
+			goto nest_cancel;
+		nla_nest_end(msg, attr);
+	}
+
+	return ret;
+
+nest_cancel:
+	nla_nest_cancel(msg, attr);
+	return ret;
+}
+
+static int
+__dpll_cmd_dump_status(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	int ret = dpll_msg_add_source_pin(msg, dpll);
+
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_temp(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_lock_status(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_mode(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_modes_supported(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_clock_id(msg, dpll);
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_clock_class(msg, dpll);
+
+	return ret;
+}
+
+static int
+dpll_device_dump_one(struct dpll_device *dpll, struct sk_buff *msg,
+		     int dump_filter)
+{
+	int ret;
+
+	dpll_lock(dpll);
+	ret = __dpll_cmd_device_dump_one(msg, dpll);
+	if (ret)
+		goto out_unlock;
+
+	if (dump_filter & DPLL_FILTER_STATUS) {
+		ret = __dpll_cmd_dump_status(msg, dpll);
+		if (ret) {
+			if (ret != -EMSGSIZE)
+				ret = -EAGAIN;
+			goto out_unlock;
+		}
+	}
+	if (dump_filter & DPLL_FILTER_PINS)
+		ret = __dpll_cmd_dump_pins(msg, dpll);
+	dpll_unlock(dpll);
+
+	return ret;
+out_unlock:
+	dpll_unlock(dpll);
+	return ret;
+}
+
+static int
+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
+			 struct dpll_pin *pin, struct genl_info *info)
+{
+	enum dpll_pin_signal_type st;
+	enum dpll_pin_mode mode;
+	struct nlattr *a;
+	int rem, ret = 0;
+	u32 prio, freq;
+
+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(a)) {
+		case DPLLA_PIN_SIGNAL_TYPE:
+			st = nla_get_s32(a);
+			ret = dpll_pin_signal_type_set(dpll, pin, st);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_CUSTOM_FREQ:
+			freq = nla_get_u32(a);
+			ret = dpll_pin_custom_freq_set(dpll, pin, freq);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_MODE:
+			mode = nla_get_s32(a);
+			ret = dpll_pin_mode_set(dpll, pin, mode);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_PRIO:
+			prio = nla_get_u32(a);
+			ret = dpll_pin_prio_set(dpll, pin, prio);
+			if (ret)
+				return ret;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int dpll_cmd_pin_set(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct nlattr **attrs = info->attrs;
+	struct dpll_pin *pin;
+	int pin_id;
+
+	if (!attrs[DPLLA_PIN_IDX])
+		return -EINVAL;
+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
+	dpll_lock(dpll);
+	pin = dpll_pin_get_by_idx(dpll, pin_id);
+	dpll_unlock(dpll);
+	if (!pin)
+		return -ENODEV;
+	return dpll_pin_set_from_nlattr(dpll, pin, info);
+}
+
+enum dpll_mode dpll_msg_read_mode(struct nlattr *a)
+{
+	return nla_get_s32(a);
+}
+
+u32 dpll_msg_read_source_pin_id(struct nlattr *a)
+{
+	return nla_get_u32(a);
+}
+
+static int
+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
+{
+	enum dpll_mode m;
+	struct nlattr *a;
+	int rem, ret = 0;
+	u32 source_pin;
+
+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+			  genlmsg_len(info->genlhdr), rem) {
+		switch (nla_type(a)) {
+		case DPLLA_MODE:
+			m = dpll_msg_read_mode(a);
+
+			ret = dpll_mode_set(dpll, m);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_SOURCE_PIN_IDX:
+			source_pin = dpll_msg_read_source_pin_id(a);
+
+			ret = dpll_source_idx_set(dpll, source_pin);
+			if (ret)
+				return ret;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int dpll_cmd_device_set(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+
+	return dpll_set_from_nlattr(dpll, info);
+}
+
+static int
+dpll_cmd_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+	struct dpll_device *dpll;
+	struct nlattr *hdr;
+	unsigned long i;
+	int ret;
+
+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+			  &dpll_gnl_family, 0, DPLL_CMD_DEVICE_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	for_each_dpll(dpll, i) {
+		ret = dpll_device_dump_one(dpll, skb, ctx->dump_filter);
+		if (ret)
+			break;
+	}
+
+	if (ret)
+		genlmsg_cancel(skb, hdr);
+	else
+		genlmsg_end(skb, hdr);
+
+	return ret;
+}
+
+static int dpll_cmd_device_get(struct sk_buff *skb, struct genl_info *info)
+{
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct nlattr **attrs = info->attrs;
+	struct sk_buff *msg;
+	int dump_filter = 0;
+	struct nlattr *hdr;
+	int ret;
+
+	if (attrs[DPLLA_FILTER])
+		dump_filter = nla_get_s32(attrs[DPLLA_FILTER]);
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	hdr = genlmsg_put_reply(msg, info, &dpll_gnl_family, 0,
+				DPLL_CMD_DEVICE_GET);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	ret = dpll_device_dump_one(dpll, msg, dump_filter);
+	if (ret)
+		goto out_free_msg;
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+
+out_free_msg:
+	nlmsg_free(msg);
+	return ret;
+
+}
+
+static int dpll_cmd_device_get_start(struct netlink_callback *cb)
+{
+	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+	struct nlattr *attr = info->attrs[DPLLA_FILTER];
+
+	if (attr)
+		ctx->dump_filter = nla_get_s32(attr);
+	else
+		ctx->dump_filter = 0;
+
+	return 0;
+}
+
+static int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+			 struct genl_info *info)
+{
+	struct dpll_device *dpll_id = NULL, *dpll_name = NULL;
+
+	if (!info->attrs[DPLLA_ID] &&
+	    !info->attrs[DPLLA_NAME])
+		return -EINVAL;
+
+	if (info->attrs[DPLLA_ID]) {
+		u32 id = nla_get_u32(info->attrs[DPLLA_ID]);
+
+		dpll_id = dpll_device_get_by_id(id);
+		if (!dpll_id)
+			return -ENODEV;
+		info->user_ptr[0] = dpll_id;
+	}
+	if (info->attrs[DPLLA_NAME]) {
+		const char *name = nla_data(info->attrs[DPLLA_NAME]);
+
+		dpll_name = dpll_device_get_by_name(name);
+		if (!dpll_name)
+			return -ENODEV;
+
+		if (dpll_id && dpll_name != dpll_id)
+			return -EINVAL;
+		info->user_ptr[0] = dpll_name;
+	}
+
+	return 0;
+}
+
+static const struct genl_ops dpll_ops[] = {
+	{
+		.cmd	= DPLL_CMD_DEVICE_GET,
+		.flags  = GENL_UNS_ADMIN_PERM,
+		.start	= dpll_cmd_device_get_start,
+		.dumpit	= dpll_cmd_device_dump,
+		.doit	= dpll_cmd_device_get,
+		.policy	= dpll_cmd_device_get_policy,
+		.maxattr = ARRAY_SIZE(dpll_cmd_device_get_policy) - 1,
+	},
+	{
+		.cmd	= DPLL_CMD_DEVICE_SET,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= dpll_cmd_device_set,
+		.policy	= dpll_cmd_device_set_policy,
+		.maxattr = ARRAY_SIZE(dpll_cmd_device_set_policy) - 1,
+	},
+	{
+		.cmd	= DPLL_CMD_PIN_SET,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.doit	= dpll_cmd_pin_set,
+		.policy	= dpll_cmd_pin_set_policy,
+		.maxattr = ARRAY_SIZE(dpll_cmd_pin_set_policy) - 1,
+	},
+};
+
+static struct genl_family dpll_family __ro_after_init = {
+	.hdrsize	= 0,
+	.name		= DPLL_FAMILY_NAME,
+	.version	= DPLL_VERSION,
+	.ops		= dpll_ops,
+	.n_ops		= ARRAY_SIZE(dpll_ops),
+	.mcgrps		= dpll_mcgrps,
+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
+	.pre_doit	= dpll_pre_doit,
+	.parallel_ops   = true,
+};
+
+static int dpll_event_device_id(struct sk_buff *msg, struct dpll_device *dpll)
+{
+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
+
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
+
+	return ret;
+}
+
+static int dpll_event_device_change(struct sk_buff *msg,
+				    struct dpll_device *dpll,
+				    struct dpll_pin *pin,
+				    enum dpll_event_change event)
+{
+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
+
+	if (ret)
+		return ret;
+	ret = nla_put_s32(msg, DPLLA_CHANGE_TYPE, event);
+	if (ret)
+		return ret;
+	switch (event)	{
+	case DPLL_CHANGE_PIN_ADD:
+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
+	case DPLL_CHANGE_PIN_MODE:
+	case DPLL_CHANGE_PIN_PRIO:
+		ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * Generic netlink DPLL event encoding
+ */
+static int dpll_send_event_create(enum dpll_event event,
+				  struct dpll_device *dpll)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
+	if (!hdr)
+		goto out_free_msg;
+
+	ret = dpll_event_device_id(msg, dpll);
+	if (ret)
+		goto out_cancel_msg;
+	genlmsg_end(msg, hdr);
+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+out_cancel_msg:
+	genlmsg_cancel(msg, hdr);
+out_free_msg:
+	nlmsg_free(msg);
+
+	return ret;
+}
+
+/*
+ * Generic netlink DPLL event encoding
+ */
+static int dpll_send_event_change(struct dpll_device *dpll,
+				  struct dpll_pin *pin,
+				  enum dpll_event_change event)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, DPLL_EVENT_DEVICE_CHANGE);
+	if (!hdr)
+		goto out_free_msg;
+
+	ret = dpll_event_device_change(msg, dpll, pin, event);
+	if (ret)
+		goto out_cancel_msg;
+	genlmsg_end(msg, hdr);
+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
+
+	return 0;
+
+out_cancel_msg:
+	genlmsg_cancel(msg, hdr);
+out_free_msg:
+	nlmsg_free(msg);
+
+	return ret;
+}
+
+int dpll_notify_device_create(struct dpll_device *dpll)
+{
+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
+}
+
+int dpll_notify_device_delete(struct dpll_device *dpll)
+{
+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
+}
+
+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change event)
+{
+	return dpll_send_event_change(dpll, NULL, event);
+}
+EXPORT_SYMBOL_GPL(dpll_device_notify);
+
+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dpll_event_change event)
+{
+	return dpll_send_event_change(dpll, pin, event);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_notify);
+
+int __init dpll_netlink_init(void)
+{
+	return genl_register_family(&dpll_family);
+}
+
+void dpll_netlink_finish(void)
+{
+	genl_unregister_family(&dpll_family);
+}
+
+void __exit dpll_netlink_fini(void)
+{
+	dpll_netlink_finish();
+}
diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
new file mode 100644
index 000000000000..8e50b2493027
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+/**
+ * dpll_notify_device_create - notify that the device has been created
+ * @dpll: registered dpll pointer
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_notify_device_create(struct dpll_device *dpll);
+
+
+/**
+ * dpll_notify_device_delete - notify that the device has been deleted
+ * @dpll: registered dpll pointer
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_notify_device_delete(struct dpll_device *dpll);
+
+int __init dpll_netlink_init(void);
+void dpll_netlink_finish(void);
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
new file mode 100644
index 000000000000..fcba46ea1b7b
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
+ */
+
+#ifndef __DPLL_H__
+#define __DPLL_H__
+
+#include <uapi/linux/dpll.h>
+#include <linux/device.h>
+
+struct dpll_device;
+struct dpll_pin;
+
+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
+
+struct dpll_device_ops {
+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode);
+	int (*mode_set)(const struct dpll_device *dpll,
+			const enum dpll_mode mode);
+	bool (*mode_supported)(const struct dpll_device *dpll,
+			       const enum dpll_mode mode);
+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
+				  u32 *pin_idx);
+	int (*lock_status_get)(const struct dpll_device *dpll,
+			       enum dpll_lock_status *status);
+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp);
+};
+
+struct dpll_pin_ops {
+	int (*signal_type_get)(const struct dpll_device *dpll,
+			       const struct dpll_pin *pin,
+			       enum dpll_pin_signal_type *type);
+	int (*signal_type_set)(const struct dpll_device *dpll,
+			       const struct dpll_pin *pin,
+			       const enum dpll_pin_signal_type type);
+	bool (*signal_type_supported)(const struct dpll_device *dpll,
+				      const struct dpll_pin *pin,
+				      const enum dpll_pin_signal_type type);
+	int (*custom_freq_set)(const struct dpll_device *dpll,
+			       const struct dpll_pin *pin,
+			       const u32 custom_freq);
+	int (*custom_freq_get)(const struct dpll_device *dpll,
+			       const struct dpll_pin *pin,
+			       u32 *custom_freq);
+	bool (*mode_active)(const struct dpll_device *dpll,
+			     const struct dpll_pin *pin,
+			     const enum dpll_pin_mode mode);
+	int (*mode_enable)(const struct dpll_device *dpll,
+			    const struct dpll_pin *pin,
+			    const enum dpll_pin_mode mode);
+	bool (*mode_supported)(const struct dpll_device *dpll,
+				const struct dpll_pin *pin,
+				const enum dpll_pin_mode mode);
+	int (*prio_get)(const struct dpll_device *dpll,
+			const struct dpll_pin *pin,
+			u32 *prio);
+	int (*prio_set)(const struct dpll_device *dpll,
+			const struct dpll_pin *pin,
+			const u32 prio);
+	int (*net_if_idx_get)(const struct dpll_device *dpll,
+			      const struct dpll_pin *pin,
+			      int *net_if_idx);
+	int (*select)(const struct dpll_device *dpll,
+		      const struct dpll_pin *pin);
+};
+
+/**
+ * dpll_device_alloc - allocate memory for a new dpll_device object
+ * @ops: pointer to dpll operations structure
+ * @type: type of a dpll being allocated
+ * @clock_id: a system unique number for a device
+ * @clock_class: quality class of a DPLL clock
+ * @dev_driver_idx: index of dpll device on parent device
+ * @priv: private data of a registerer
+ * @parent: device structure of a module registering dpll device
+ *
+ * Allocate memory for a new dpll and initialize it with its type, name,
+ * callbacks and private data pointer.
+ *
+ * Name is generated based on: parent driver, type and dev_driver_idx.
+ * Finding allocated and registered dpll device is also possible with
+ * the: clock_id, type and dev_driver_idx. This way dpll device can be
+ * shared by multiple instances of a device driver.
+ *
+ * Returns:
+ * * pointer to initialized dpll - success
+ * * NULL - memory allocation fail
+ */
+struct dpll_device
+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
+		   const u64 clock_id, enum dpll_clock_class clock_class,
+		   u8 dev_driver_idx, void *priv, struct device *parent);
+
+/**
+ * dpll_device_unregister - unregister registered dpll
+ * @dpll: pointer to dpll
+ *
+ * Unregister the dpll from the subsystem, make it unavailable for netlink
+ * API users.
+ */
+void dpll_device_unregister(struct dpll_device *dpll);
+
+/**
+ * dpll_device_free - free dpll memory
+ * @dpll: pointer to dpll
+ *
+ * Free memory allocated with ``dpll_device_alloc(..)``
+ */
+void dpll_device_free(struct dpll_device *dpll);
+
+/**
+ * dpll_priv - get private data
+ * @dpll: pointer to dpll
+ *
+ * Obtain private data pointer passed to dpll subsystem when allocating
+ * device with ``dpll_device_alloc(..)``
+ */
+void *dpll_priv(const struct dpll_device *dpll);
+
+/**
+ * dpll_pin_priv - get private data
+ * @dpll: pointer to dpll
+ *
+ * Obtain private pin data pointer passed to dpll subsystem when pin
+ * was registered with dpll.
+ */
+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
+
+/**
+ * dpll_pin_idx - get pin idx
+ * @dpll: pointer to dpll
+ * @pin: pointer to a pin
+ *
+ * Obtain pin index of given pin on given dpll.
+ *
+ * Return: PIN_IDX_INVALID - if failed to find pin, otherwise pin index
+ */
+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
+
+/**
+ * dpll_shared_pin_register - share a pin between dpll devices
+ * @dpll_pin_owner: a dpll already registered with a pin
+ * @dpll: a dpll being registered with a pin
+ * @shared_pin_description: identifies pin registered with dpll device
+ *	(@dpll_pin_owner) which is now being registered with new dpll (@dpll)
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ *
+ * Register a pin already registered with different dpll device.
+ * Allow to share a single pin within multiple dpll instances.
+ *
+ * Returns:
+ * * 0 - success
+ * * negative - failure
+ */
+int
+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
+			 struct dpll_device *dpll,
+			 const char *shared_pin_description,
+			 struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_pin_alloc - allocate memory for a new dpll_pin object
+ * @description: pointer to string description of a pin with max length
+ * equal to PIN_DESC_LEN
+ * @type: type of allocated pin
+ *
+ * Allocate memory for a new pin and initialize its resources.
+ *
+ * Returns:
+ * * pointer to initialized pin - success
+ * * NULL - memory allocation fail
+ */
+struct dpll_pin *dpll_pin_alloc(const char *description,
+				const enum dpll_pin_type type);
+
+/**
+ * dpll_pin_register - register pin with a dpll device
+ * @dpll: pointer to dpll object to register pin with
+ * @pin: pointer to allocated pin object being registered with dpll
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops
+ *
+ * Register previously allocated pin object with a dpll device.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this dpll,
+ * * -EBUSY - couldn't allocate id for a pin.
+ */
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+		      struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_pin_deregister - deregister pin from a dpll device
+ * @dpll: pointer to dpll object to deregister pin from
+ * @pin: pointer to allocated pin object being deregistered from dpll
+ *
+ * Deregister previously registered pin object from a dpll device.
+ *
+ * Return:
+ * * 0 - pin was successfully deregistered from this dpll device,
+ * * -ENXIO - given pin was not registered with this dpll device,
+ * * -EINVAL - pin pointer is not valid.
+ */
+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin);
+
+/**
+ * dpll_pin_free - free memory allocated for a pin
+ * @pin: pointer to allocated pin object being freed
+ *
+ * Shared pins must be deregistered from all dpll devices before freeing them,
+ * otherwise the memory won't be freed.
+ */
+void dpll_pin_free(struct dpll_pin *pin);
+
+/**
+ * dpll_muxed_pin_register - register a pin to a muxed-type pin
+ * @parent_pin_description: parent pin description as given on it's allocation
+ * @pin: pointer to allocated pin object being registered with a parent pin
+ * @ops: struct with pin ops callbacks
+ * @priv: private data pointer passed when calling callback ops*
+ *
+ * In case of multiplexed pins, allows registring them under a single
+ * parent pin.
+ *
+ * Return:
+ * * 0 - if pin was registered with a parent pin,
+ * * -ENOMEM - failed to allocate memory,
+ * * -EEXIST - pin already registered with this parent pin,
+ * * -EBUSY - couldn't assign id for a pin.
+ */
+int dpll_muxed_pin_register(struct dpll_device *dpll,
+			    const char *parent_pin_description,
+			    struct dpll_pin *pin,
+			    struct dpll_pin_ops *ops, void *priv);
+
+/**
+ * dpll_device_get_by_clock_id - find a dpll by its clock_id, type and index
+ * @clock_id: clock_id of dpll, as given by driver on ``dpll_device_alloc``
+ * @type: type of dpll, as given by driver on ``dpll_device_alloc``
+ * @idx: index of dpll, as given by driver on ``dpll_device_alloc``
+ *
+ * Allows multiple driver instances using one physical DPLL to find
+ * and share already registered DPLL device.
+ *
+ * Return: pointer if device was found, NULL otherwise.
+ */
+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
+						enum dpll_type type, u8 idx);
+
+/**
+ * dpll_device_notify - notify on dpll device change
+ * @dpll: dpll device pointer
+ * @event: type of change
+ *
+ * Broadcast event to the netlink multicast registered listeners.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change event);
+
+/**
+ * dpll_pin_notify - notify on dpll pin change
+ * @dpll: dpll device pointer
+ * @pin: dpll pin pointer
+ * @event: type of change
+ *
+ * Broadcast event to the netlink multicast registered listeners.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
+		    enum dpll_event_change event);
+
+#endif
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
new file mode 100644
index 000000000000..b7dbdd814b5c
--- /dev/null
+++ b/include/uapi/linux/dpll.h
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_DPLL_H
+#define _UAPI_LINUX_DPLL_H
+
+#define DPLL_NAME_LEN		32
+#define DPLL_DESC_LEN		20
+#define DPLL_PIN_DESC_LEN	20
+
+/* Adding event notification support elements */
+#define DPLL_FAMILY_NAME	"dpll"
+#define DPLL_VERSION		0x01
+#define DPLL_MONITOR_GROUP_NAME	"monitor"
+
+#define DPLL_FILTER_PINS	1
+#define DPLL_FILTER_STATUS	2
+
+/* dplla - Attributes of dpll generic netlink family
+ *
+ * @DPLLA_UNSPEC - invalid attribute
+ * @DPLLA_ID - ID of a dpll device (unsigned int)
+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH size)
+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum dpll_mode)
+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll
+ *	(unsigned int)
+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)
+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum dpll_clock_class)
+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests (int,
+ *	sum of DPLL_DUMP_FILTER_* defines)
+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by driver
+ *	(char array of PIN_DESC_LEN size)
+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
+ *	(enum dpll_pin_signal_type)
+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
+ *	(enum dpll_pin_signal_type)
+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
+ *	(unsigned int)
+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
+ *	(enum dpll_pin_mode)
+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
+ * @DPLLA_CHANGE_TYPE - type of device change event
+ *	(enum dpll_change_type)
+ **/
+enum dplla {
+	DPLLA_UNSPEC,
+	DPLLA_ID,
+	DPLLA_NAME,
+	DPLLA_MODE,
+	DPLLA_MODE_SUPPORTED,
+	DPLLA_SOURCE_PIN_IDX,
+	DPLLA_LOCK_STATUS,
+	DPLLA_TEMP,
+	DPLLA_CLOCK_ID,
+	DPLLA_CLOCK_CLASS,
+	DPLLA_FILTER,
+	DPLLA_PIN,
+	DPLLA_PIN_IDX,
+	DPLLA_PIN_DESCRIPTION,
+	DPLLA_PIN_TYPE,
+	DPLLA_PIN_SIGNAL_TYPE,
+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
+	DPLLA_PIN_CUSTOM_FREQ,
+	DPLLA_PIN_MODE,
+	DPLLA_PIN_MODE_SUPPORTED,
+	DPLLA_PIN_PRIO,
+	DPLLA_PIN_PARENT_IDX,
+	DPLLA_PIN_NETIFINDEX,
+	DPLLA_CHANGE_TYPE,
+	__DPLLA_MAX,
+};
+
+#define DPLLA_MAX (__DPLLA_MAX - 1)
+
+/* dpll_lock_status - DPLL status provides information of device status
+ *
+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid (or is in
+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid signal
+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a valid lock
+ *	or was forced by DPLL_MODE_HOLDOVER mode)
+ **/
+enum dpll_lock_status {
+	DPLL_LOCK_STATUS_UNSPEC,
+	DPLL_LOCK_STATUS_UNLOCKED,
+	DPLL_LOCK_STATUS_CALIBRATING,
+	DPLL_LOCK_STATUS_LOCKED,
+	DPLL_LOCK_STATUS_HOLDOVER,
+
+	__DPLL_LOCK_STATUS_MAX,
+};
+
+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
+
+/* dpll_pin_type - signal types
+ *
+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
+ * @DPLL_PIN_TYPE_EXT - external source
+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
+ **/
+enum dpll_pin_type {
+	DPLL_PIN_TYPE_UNSPEC,
+	DPLL_PIN_TYPE_MUX,
+	DPLL_PIN_TYPE_EXT,
+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+	DPLL_PIN_TYPE_INT_OSCILLATOR,
+	DPLL_PIN_TYPE_GNSS,
+
+	__DPLL_PIN_TYPE_MAX,
+};
+
+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
+
+/* dpll_pin_signal_type - signal types
+ *
+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value defined
+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
+ **/
+enum dpll_pin_signal_type {
+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
+
+	__DPLL_PIN_SIGNAL_TYPE_MAX,
+};
+
+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
+
+/* dpll_pin_mode - available pin states
+ *
+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
+ * @DPLL_PIN_MODE_CONNECTED - pin connected
+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
+ **/
+enum dpll_pin_mode {
+	DPLL_PIN_MODE_UNSPEC,
+	DPLL_PIN_MODE_CONNECTED,
+	DPLL_PIN_MODE_DISCONNECTED,
+	DPLL_PIN_MODE_SOURCE,
+	DPLL_PIN_MODE_OUTPUT,
+
+	__DPLL_PIN_MODE_MAX,
+};
+
+#define DPLL_PIN_MODE_MAX (__DPLL_PIN_MODE_MAX - 1)
+
+/**
+ * dpll_event - Events of dpll generic netlink family
+ *
+ * @DPLL_EVENT_UNSPEC - invalid event type
+ * @DPLL_EVENT_DEVICE_CREATE - dpll device created
+ * @DPLL_EVENT_DEVICE_DELETE - dpll device deleted
+ * @DPLL_EVENT_DEVICE_CHANGE - attribute of dpll device or pin changed
+ **/
+enum dpll_event {
+	DPLL_EVENT_UNSPEC,
+	DPLL_EVENT_DEVICE_CREATE,
+	DPLL_EVENT_DEVICE_DELETE,
+	DPLL_EVENT_DEVICE_CHANGE,
+
+	__DPLL_EVENT_MAX,
+};
+
+#define DPLL_EVENT_MAX (__DPLL_EVENT_MAX - 1)
+
+/**
+ * dpll_change_type - values of events in case of device change event
+ * (DPLL_EVENT_DEVICE_CHANGE)
+ *
+ * @DPLL_CHANGE_UNSPEC - invalid event type
+ * @DPLL_CHANGE_MODE - mode changed
+ * @DPLL_CHANGE_LOCK_STATUS - lock status changed
+ * @DPLL_CHANGE_SOURCE_PIN - source pin changed,
+ * @DPLL_CHANGE_TEMP - temperature changed
+ * @DPLL_CHANGE_PIN_ADD - source pin added,
+ * @DPLL_CHANGE_PIN_DEL - source pin deleted,
+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
+ * @DPLL_CHANGE_PIN_MODE - pin state changed
+ * @DPLL_CHANGE_PIN_PRIO - pin prio changed
+ **/
+enum dpll_event_change {
+	DPLL_CHANGE_UNSPEC,
+	DPLL_CHANGE_MODE,
+	DPLL_CHANGE_LOCK_STATUS,
+	DPLL_CHANGE_SOURCE_PIN,
+	DPLL_CHANGE_TEMP,
+	DPLL_CHANGE_PIN_ADD,
+	DPLL_CHANGE_PIN_DEL,
+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
+	DPLL_CHANGE_PIN_MODE,
+	DPLL_CHANGE_PIN_PRIO,
+
+	__DPLL_CHANGE_MAX,
+};
+
+#define DPLL_CHANGE_MAX (__DPLL_CHANGE_MAX - 1)
+
+/**
+ * dpll_cmd - Commands supported by the dpll generic netlink family
+ *
+ * @DPLL_CMD_UNSPEC - invalid message type
+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes of
+ *	single dpll device and it's pins
+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
+ **/
+enum dpll_cmd {
+	DPLL_CMD_UNSPEC,
+	DPLL_CMD_DEVICE_GET,
+	DPLL_CMD_DEVICE_SET,
+	DPLL_CMD_PIN_SET,
+
+	__DPLL_CMD_MAX,
+};
+
+#define DPLL_CMD_MAX (__DPLL_CMD_MAX - 1)
+
+/**
+ * dpll_mode - Working-modes a dpll can support. Modes differentiate how
+ * dpll selects one of its sources to syntonize with a source.
+ *
+ * @DPLL_MODE_UNSPEC - invalid
+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request to dpll
+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by dpll
+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
+ **/
+enum dpll_mode {
+	DPLL_MODE_UNSPEC,
+	DPLL_MODE_MANUAL,
+	DPLL_MODE_AUTOMATIC,
+	DPLL_MODE_HOLDOVER,
+	DPLL_MODE_FREERUN,
+	DPLL_MODE_NCO,
+
+	__DPLL_MODE_MAX,
+};
+
+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
+
+/**
+ * dpll_clock_class - enumerate quality class of a DPLL clock as specified in
+ * Recommendation ITU-T G.8273.2/Y.1368.2.
+ */
+enum dpll_clock_class {
+	DPLL_CLOCK_CLASS_UNSPEC,
+	DPLL_CLOCK_CLASS_A,
+	DPLL_CLOCK_CLASS_B,
+	DPLL_CLOCK_CLASS_C,
+
+	__DPLL_CLOCK_CLASS_MAX,
+};
+
+#define DPLL_CLOCK_CLASS_MAX (__DPLL_CLOCK_CLASS_MAX - 1)
+
+/**
+ * enum dpll_type - type of dpll, integer value of enum is embedded into
+ * name of DPLL device (DPLLA_NAME)
+ *
+ * @DPLL_TYPE_UNSPEC - unspecified
+ * @DPLL_TYPE_PPS - dpll produces Pulse-Per-Second signal
+ * @DPLL_TYPE_EEC - dpll drives the Ethernet Equipment Clock
+ */
+enum dpll_type {
+	DPLL_TYPE_UNSPEC,
+	DPLL_TYPE_PPS,
+	DPLL_TYPE_EEC,
+
+	__DPLL_TYPE_MAX
+};
+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
+
+#endif /* _UAPI_LINUX_DPLL_H */
-- 
2.30.2


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

* [RFC PATCH v5 2/4] dpll: documentation on DPLL subsystem interface
  2023-01-17 18:00 ` Vadim Fedorenko
@ 2023-01-17 18:00   ` Vadim Fedorenko
  -1 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, netdev, linux-arm-kernel, linux-clk

Add documentation explaining common netlink interface to configure DPLL
devices and monitoring events. Common way to implement DPLL device in
a driver is also covered.

Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
---
 Documentation/networking/dpll.rst  | 280 +++++++++++++++++++++++++++++
 Documentation/networking/index.rst |   1 +
 2 files changed, 281 insertions(+)
 create mode 100644 Documentation/networking/dpll.rst

diff --git a/Documentation/networking/dpll.rst b/Documentation/networking/dpll.rst
new file mode 100644
index 000000000000..bd4d5f9f60ce
--- /dev/null
+++ b/Documentation/networking/dpll.rst
@@ -0,0 +1,280 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================
+The Linux kernel DPLL subsystem
+===============================
+
+
+The main purpose of DPLL subsystem is to provide general interface
+to configure devices that use any kind of Digital PLL and could use
+different sources of signal to synchronize to as well as different
+types of outputs.
+The main interface is NETLINK_GENERIC based protocol with an event
+monitoring multicast group defined.
+
+
+Pin object
+==========
+A pin is amorphic object which represents either input and output, it
+could be internal component of the device, as well as externaly
+connected.
+The number of pins per dpll vary, but usually multiple pins shall be
+provided for a single dpll device.
+Direction of a pin and it's capabilities are provided to the user in
+response for netlink dump request messages.
+Pin can be shared by multiple dpll devices. Where configuration on one
+pin can alter multiple dplls (i.e. DPLL_PIN_SIGNAL_TYPE, DPLL_PIN_MODE),
+or just one pin-dpll pair (i.e. DPLL_PIN_PRIO).
+Pin can be also a MUX type, where one or more pins are attached to
+a parent pin. The parent pin is the one directly connected to the dpll,
+which may be used by dplls in DPLL_MODE_AUTOMATIC selection mode, where
+only pins directly connected to the dpll are capable of automatic
+source pin selection. In such case, pins are dumped with
+DPLLA_PIN_PARENT_IDX, and are able to be selected by the userspace with
+netlink request.
+
+Configuration commands group
+============================
+
+Configuration commands are used to get or dump information about
+registered DPLL devices (and pins), as well as set configuration of
+device or pins. As DPLL device could not be abstract and reflects real
+hardware, there is no way to add new DPLL device via netlink from user
+space and each device should be registered by it's driver.
+
+All netlink commands require ``GENL_UNS_ADMIN_PERM``. This is to prevent
+any spamming/D.o.S. from unauthorized userspace applications.
+
+List of command with possible attributes
+========================================
+
+All constants identifying command types use ``DPLL_CMD_`` prefix and
+suffix according to command purpose. All attributes use ``DPLLA_``
+prefix and suffix according to attribute purpose:
+
+  ============================  =======================================
+  ``DEVICE_GET``                userspace to get device info
+    ``ID``                      attr internal dpll device index
+    ``NAME``                    attr dpll device name
+    ``MODE``                    attr selection mode
+    ``MODE_SUPPORTED``          attr available selection modes
+    ``SOURCE_PIN_IDX``          attr index of currently selected source
+    ``LOCK_STATUS``             attr internal frequency-lock status
+    ``TEMP``                    attr device temperature information
+    ``CLOCK_ID``                attr Unique Clock Identifier (EUI-64),
+                                as defined by the IEEE 1588 standard
+    ``CLOCK_CLASS``             attr clock class, as defined in
+                                recommendation ITU-T G.8273.2/Y.1368.2
+    ``FILTER``                  attr for filtering dump or get requests
+  ``DEVICE_SET``                userspace to set dpll device
+                                configuration
+    ``ID``                      attr internal dpll device index
+    ``NAME``                    attr dpll device name (not required if
+                                dpll device index was provided)
+    ``MODE``                    attr selection mode to configure
+    ``PIN_IDX``                 attr index of source pin to select as
+                                active source
+  ``PIN_SET``                   userspace to set pins configuration
+    ``ID``                      attr internal dpll device index
+    ``NAME``                    attr dpll device name (not required if
+                                dpll device index was provided)
+    ``PIN_IDX``                 attr index of a pin to configure
+    ``PIN_SIGNAL_TYPE``         attr signal type configuration value
+                                for selected pin
+    ``PIN_CUSTOM_FREQ``         attr signal custom frequency to be set
+    ``PIN_MODE``                attr pin mode to be set
+    ``PIN_PRIO``                attr pin priority to be set
+
+Netlink dump requests
+=====================
+All below attributes of dump requests use ``DPLLA_`` prefix.
+
+The ``DEVICE_GET`` command is capable of dump type netlink requests.
+In such case the userspace shall provide ``FILTER`` attribute
+value to filter the response as required.
+If filter is not provided only name and id of available dpll(s) is
+provided. If the request also contains ``ID`` attribute, only selected
+dpll device shall be dumped.
+
+Possible response message attributes for netlink requests depending on
+the value of ``FILTER`` attribute:
+
+  =============================== ====================================
+  ``DPLL_FILTER_PINS``            value of ``FILTER`` attribute
+    ``PIN``                       attr nested type contain single pin
+                                  attributes
+    ``PIN_IDX``                   attr index of dumped pin
+    ``PIN_DESCRIPTION``           description of a pin provided by
+                                  driver
+    ``PIN_TYPE``                  attr value of pin type
+    ``PIN_SIGNAL_TYPE``           attr value of pin signal type
+    ``PIN_SIGNAL_TYPE_SUPPORTED`` attr value of supported pin signal
+                                  type
+    ``PIN_CUSTOM_FREQ``           attr value of pin custom frequency
+    ``PIN_MODE``                  attr value of pin mode
+    ``PIN_MODE_SUPPORTED``        attr value of supported pin modes
+    ``PIN_PRIO``                  attr value of pin prio
+    ``PIN_PARENT_IDX``            attr value of pin patent index
+    ``PIN_NETIFINDEX``            attr value of netdevice assocaiated
+                                  with the pin
+  ``DPLL_FILTER_STATUS``          value of ``FILTER`` attribute
+    ``ID``                        attr internal dpll device index
+    ``NAME``                      attr dpll device name
+    ``MODE``                      attr selection mode
+    ``MODE_SUPPORTED``            attr available selection modes
+    ``SOURCE_PIN_IDX``            attr index of currently selected
+                                  source
+    ``LOCK_STATUS``               attr internal frequency-lock status
+    ``TEMP``                      attr device temperature information
+    ``CLOCK_ID``                  attr Unique Clock Identifier (EUI-64),
+                                  as defined by the IEEE 1588 standard
+    ``CLOCK_CLASS``               attr clock class, as defined in
+                                  recommendation ITU-T G.8273.2/Y.1368.2
+
+
+The pre-defined enums
+=====================
+
+All the enums use the ``DPLL_`` prefix.
+
+Values for ``PIN_TYPE`` and ``PIN_TYPE_SUPPORTED`` attributes:
+
+  ============================ ========================================
+  ``PIN_TYPE_MUX``             MUX type pin, connected pins shall
+                               have their own types
+  ``PIN_TYPE_EXT``             External pin
+  ``PIN_TYPE_SYNCE_ETH_PORT``  SyncE on Ethernet port
+  ``PIN_TYPE_INT_OSCILLATOR``  Internal Oscillator (i.e. Holdover
+                               with Atomic Clock as a Source)
+  ``PIN_TYPE_GNSS``            GNSS 1PPS source
+
+Values for ``PIN_SIGNAL_TYPE`` and ``PIN_SIGNAL_TYPE_SUPPORTED``
+attributes:
+
+  ===============================  ===================================
+  ``PIN_SIGNAL_TYPE_1_PPS``        1 Hz frequency
+  ``PIN_SIGNAL_TYPE_10_MHZ``       10 MHz frequency
+  ``PIN_SIGNAL_TYPE_CUSTOM_FREQ``  Frequency value provided in attr
+                                   ``PIN_CUSTOM_FREQ``
+
+Values for ``LOCK_STATUS`` attribute:
+
+  ============================= ======================================
+  ``LOCK_STATUS_UNLOCKED``      DPLL is in freerun, not locked to any
+                                source pin
+  ``LOCK_STATUS_CALIBRATING``   DPLL device calibrates to lock to the
+                                source pin signal
+  ``LOCK_STATUS_LOCKED``        DPLL device is locked to the source
+                                pin frequency
+  ``LOCK_STATUS_HOLDOVER``      DPLL device lost a lock, using its
+                                frequency holdover capabilities
+
+Values for ``PIN_MODE`` and ``PIN_STATE_SUPPORTED`` attributes:
+
+============================= ============================
+  ``PIN_MODE_CONNECTED``     Pin connected to a dpll
+  ``PIN_MODE_DISCONNECTED``  Pin disconnected from dpll
+  ``PIN_MODE_SOURCE``        Source pin
+  ``PIN_MODE_OUTPUT``        Output pin
+
+Possible DPLL source selection mode values:
+
+  =================== ================================================
+  ``MODE_FORCED``     source pin is force-selected by
+                      ``DPLL_CMD_DEVICE_SET`` with given value of
+                      ``DPLLA_SOURCE_PIN_IDX`` attribute
+  ``MODE_AUTOMATIC``  source pin is auto selected according to
+                      configured pin priorities and source signal
+                      validity
+  ``MODE_HOLDOVER``   force holdover mode of DPLL
+  ``MODE_FREERUN``    DPLL is driven by supplied system clock without
+                      holdover capabilities
+  ``MODE_NCO``        similar to FREERUN, with possibility to
+                      numerically control frequency offset
+
+Notifications
+================
+
+DPLL device can provide notifications regarding status changes of the
+device, i.e. lock status changes, source/output type changes or alarms.
+This is the multicast group that is used to notify user-space apps via
+netlink socket:
+
+Notifications messages:
+
+  ========================= ==========================================
+  ``EVENT_DEVICE_CREATE``   event value new DPLL device was created
+    ``ID``                  attr dpll device index
+    ``NAME``                attr dpll device name
+  ``EVENT_DEVICE_DELETE``   event value DPLL device was deleted
+    ``ID``                  attr dpll device index
+  ``EVENT_DEVICE_CHANGE``   event value DPLL device attribute has changed
+    ``ID``                  attr dpll device index
+    ``CHANGE_TYPE``         attr the reason for change with values of
+                            ``enum dpll_event_change``
+
+Device change event reasons, values of ``CHANGE_TYPE`` attribute:
+
+  =========================== =========================================
+   ``CHANGE_MODE``            DPLL selection mode has changed
+   ``CHANGE_LOCK_STATUS``     DPLL lock status has changed
+   ``CHANGE_SOURCE_PIN``      DPLL source pin has changed
+   ``CHANGE_TEMP``            DPLL temperature has changed
+   ``CHANGE_PIN_ADD``         pin added to DPLL
+   ``CHANGE_PIN_DEL``         pin removed from DPLL
+   ``CHANGE_PIN_SIGNAL_TYPE`` pin signal type has changed
+   ``CHANGE_PIN_CUSTOM_FREQ`` pin custom frequency value has changed
+   ``CHANGE_PIN_MODE``        pin mode has changed
+   ``CHANGE_PIN_PRIO``        pin prio has changed
+
+
+Device driver implementation
+============================
+
+For device to operate as DPLL subsystem device, it should implement
+set of operations and register device via ``dpll_device_alloc``,
+provide the operations set, unique device clock_id, class of a clock,
+type of dpll (PPS/EEC), pointer to parent device and pointer to its
+private data, that can be used in callback ops.
+
+The pins are allocated separately with ``dpll_pin_alloc``, which
+requires providing pin description and its type.
+
+Once DPLL device is created, allocated pin can be registered with it
+with 2 different methods, always providing implemented pin callbacks,
+and private data pointer for calling them:
+``dpll_pin_register`` - simple registration with a dpll device.
+``dpll_muxed_pin_register`` - register pin with another MUX type pin.
+
+It is also possible to register pin already registered with different
+DPLL device by calling ``dpll_shared_pin_register`` - in this case
+changes requested on a single pin would affect all DPLLs which were
+registered with that pin.
+
+For different instances of a device driver requiring to find already
+registered DPLL (i.e. to connect its pins to id)
+use ``dpll_device_get_by_clock_id`` providing the same clock_id, type of
+dpll and index of the DPLL device of such type, same as given on
+original device allocation.
+
+The name od DPLL device is generated based on registerer device struct
+pointer, DPLL type and an index received from registerer device driver.
+Name is in format: ``dpll_%s_%d_%d`` witch arguments:
+``dev_name(parent)`` - syscall on parent device
+``type``             - DPLL type converted to string
+``dev_driver_idx``   - registerer given index
+
+Notifications of adding or removing DPLL devices are created within
+subsystem itself.
+Notifications about registering/deregistering pins are also invoked by
+the subsystem.
+Any other change notifications shall be requested by device driver with
+``dpll_device_notify`` or ``dpll_pin_notify`` and corresponding reason.
+Change reason enums ``dpll_event_change`` are defined in
+``<linux/dpll.h>``, constants and enums are placed in
+``<uapi/linux/dpll.h>`` to be consistent with user-space.
+
+There is no strict requirement to implement all the operations for
+each device, every operation handler is checked for existence and
+ENOTSUPP is returned in case of absence of specific handler.
+
diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
index 4f2d1f682a18..d50d98939942 100644
--- a/Documentation/networking/index.rst
+++ b/Documentation/networking/index.rst
@@ -16,6 +16,7 @@ Contents:
    device_drivers/index
    dsa/index
    devlink/index
+   dpll
    caif/index
    ethtool-netlink
    ieee802154
-- 
2.30.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v5 2/4] dpll: documentation on DPLL subsystem interface
@ 2023-01-17 18:00   ` Vadim Fedorenko
  0 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: Vadim Fedorenko, netdev, linux-arm-kernel, linux-clk

Add documentation explaining common netlink interface to configure DPLL
devices and monitoring events. Common way to implement DPLL device in
a driver is also covered.

Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
---
 Documentation/networking/dpll.rst  | 280 +++++++++++++++++++++++++++++
 Documentation/networking/index.rst |   1 +
 2 files changed, 281 insertions(+)
 create mode 100644 Documentation/networking/dpll.rst

diff --git a/Documentation/networking/dpll.rst b/Documentation/networking/dpll.rst
new file mode 100644
index 000000000000..bd4d5f9f60ce
--- /dev/null
+++ b/Documentation/networking/dpll.rst
@@ -0,0 +1,280 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================
+The Linux kernel DPLL subsystem
+===============================
+
+
+The main purpose of DPLL subsystem is to provide general interface
+to configure devices that use any kind of Digital PLL and could use
+different sources of signal to synchronize to as well as different
+types of outputs.
+The main interface is NETLINK_GENERIC based protocol with an event
+monitoring multicast group defined.
+
+
+Pin object
+==========
+A pin is amorphic object which represents either input and output, it
+could be internal component of the device, as well as externaly
+connected.
+The number of pins per dpll vary, but usually multiple pins shall be
+provided for a single dpll device.
+Direction of a pin and it's capabilities are provided to the user in
+response for netlink dump request messages.
+Pin can be shared by multiple dpll devices. Where configuration on one
+pin can alter multiple dplls (i.e. DPLL_PIN_SIGNAL_TYPE, DPLL_PIN_MODE),
+or just one pin-dpll pair (i.e. DPLL_PIN_PRIO).
+Pin can be also a MUX type, where one or more pins are attached to
+a parent pin. The parent pin is the one directly connected to the dpll,
+which may be used by dplls in DPLL_MODE_AUTOMATIC selection mode, where
+only pins directly connected to the dpll are capable of automatic
+source pin selection. In such case, pins are dumped with
+DPLLA_PIN_PARENT_IDX, and are able to be selected by the userspace with
+netlink request.
+
+Configuration commands group
+============================
+
+Configuration commands are used to get or dump information about
+registered DPLL devices (and pins), as well as set configuration of
+device or pins. As DPLL device could not be abstract and reflects real
+hardware, there is no way to add new DPLL device via netlink from user
+space and each device should be registered by it's driver.
+
+All netlink commands require ``GENL_UNS_ADMIN_PERM``. This is to prevent
+any spamming/D.o.S. from unauthorized userspace applications.
+
+List of command with possible attributes
+========================================
+
+All constants identifying command types use ``DPLL_CMD_`` prefix and
+suffix according to command purpose. All attributes use ``DPLLA_``
+prefix and suffix according to attribute purpose:
+
+  ============================  =======================================
+  ``DEVICE_GET``                userspace to get device info
+    ``ID``                      attr internal dpll device index
+    ``NAME``                    attr dpll device name
+    ``MODE``                    attr selection mode
+    ``MODE_SUPPORTED``          attr available selection modes
+    ``SOURCE_PIN_IDX``          attr index of currently selected source
+    ``LOCK_STATUS``             attr internal frequency-lock status
+    ``TEMP``                    attr device temperature information
+    ``CLOCK_ID``                attr Unique Clock Identifier (EUI-64),
+                                as defined by the IEEE 1588 standard
+    ``CLOCK_CLASS``             attr clock class, as defined in
+                                recommendation ITU-T G.8273.2/Y.1368.2
+    ``FILTER``                  attr for filtering dump or get requests
+  ``DEVICE_SET``                userspace to set dpll device
+                                configuration
+    ``ID``                      attr internal dpll device index
+    ``NAME``                    attr dpll device name (not required if
+                                dpll device index was provided)
+    ``MODE``                    attr selection mode to configure
+    ``PIN_IDX``                 attr index of source pin to select as
+                                active source
+  ``PIN_SET``                   userspace to set pins configuration
+    ``ID``                      attr internal dpll device index
+    ``NAME``                    attr dpll device name (not required if
+                                dpll device index was provided)
+    ``PIN_IDX``                 attr index of a pin to configure
+    ``PIN_SIGNAL_TYPE``         attr signal type configuration value
+                                for selected pin
+    ``PIN_CUSTOM_FREQ``         attr signal custom frequency to be set
+    ``PIN_MODE``                attr pin mode to be set
+    ``PIN_PRIO``                attr pin priority to be set
+
+Netlink dump requests
+=====================
+All below attributes of dump requests use ``DPLLA_`` prefix.
+
+The ``DEVICE_GET`` command is capable of dump type netlink requests.
+In such case the userspace shall provide ``FILTER`` attribute
+value to filter the response as required.
+If filter is not provided only name and id of available dpll(s) is
+provided. If the request also contains ``ID`` attribute, only selected
+dpll device shall be dumped.
+
+Possible response message attributes for netlink requests depending on
+the value of ``FILTER`` attribute:
+
+  =============================== ====================================
+  ``DPLL_FILTER_PINS``            value of ``FILTER`` attribute
+    ``PIN``                       attr nested type contain single pin
+                                  attributes
+    ``PIN_IDX``                   attr index of dumped pin
+    ``PIN_DESCRIPTION``           description of a pin provided by
+                                  driver
+    ``PIN_TYPE``                  attr value of pin type
+    ``PIN_SIGNAL_TYPE``           attr value of pin signal type
+    ``PIN_SIGNAL_TYPE_SUPPORTED`` attr value of supported pin signal
+                                  type
+    ``PIN_CUSTOM_FREQ``           attr value of pin custom frequency
+    ``PIN_MODE``                  attr value of pin mode
+    ``PIN_MODE_SUPPORTED``        attr value of supported pin modes
+    ``PIN_PRIO``                  attr value of pin prio
+    ``PIN_PARENT_IDX``            attr value of pin patent index
+    ``PIN_NETIFINDEX``            attr value of netdevice assocaiated
+                                  with the pin
+  ``DPLL_FILTER_STATUS``          value of ``FILTER`` attribute
+    ``ID``                        attr internal dpll device index
+    ``NAME``                      attr dpll device name
+    ``MODE``                      attr selection mode
+    ``MODE_SUPPORTED``            attr available selection modes
+    ``SOURCE_PIN_IDX``            attr index of currently selected
+                                  source
+    ``LOCK_STATUS``               attr internal frequency-lock status
+    ``TEMP``                      attr device temperature information
+    ``CLOCK_ID``                  attr Unique Clock Identifier (EUI-64),
+                                  as defined by the IEEE 1588 standard
+    ``CLOCK_CLASS``               attr clock class, as defined in
+                                  recommendation ITU-T G.8273.2/Y.1368.2
+
+
+The pre-defined enums
+=====================
+
+All the enums use the ``DPLL_`` prefix.
+
+Values for ``PIN_TYPE`` and ``PIN_TYPE_SUPPORTED`` attributes:
+
+  ============================ ========================================
+  ``PIN_TYPE_MUX``             MUX type pin, connected pins shall
+                               have their own types
+  ``PIN_TYPE_EXT``             External pin
+  ``PIN_TYPE_SYNCE_ETH_PORT``  SyncE on Ethernet port
+  ``PIN_TYPE_INT_OSCILLATOR``  Internal Oscillator (i.e. Holdover
+                               with Atomic Clock as a Source)
+  ``PIN_TYPE_GNSS``            GNSS 1PPS source
+
+Values for ``PIN_SIGNAL_TYPE`` and ``PIN_SIGNAL_TYPE_SUPPORTED``
+attributes:
+
+  ===============================  ===================================
+  ``PIN_SIGNAL_TYPE_1_PPS``        1 Hz frequency
+  ``PIN_SIGNAL_TYPE_10_MHZ``       10 MHz frequency
+  ``PIN_SIGNAL_TYPE_CUSTOM_FREQ``  Frequency value provided in attr
+                                   ``PIN_CUSTOM_FREQ``
+
+Values for ``LOCK_STATUS`` attribute:
+
+  ============================= ======================================
+  ``LOCK_STATUS_UNLOCKED``      DPLL is in freerun, not locked to any
+                                source pin
+  ``LOCK_STATUS_CALIBRATING``   DPLL device calibrates to lock to the
+                                source pin signal
+  ``LOCK_STATUS_LOCKED``        DPLL device is locked to the source
+                                pin frequency
+  ``LOCK_STATUS_HOLDOVER``      DPLL device lost a lock, using its
+                                frequency holdover capabilities
+
+Values for ``PIN_MODE`` and ``PIN_STATE_SUPPORTED`` attributes:
+
+============================= ============================
+  ``PIN_MODE_CONNECTED``     Pin connected to a dpll
+  ``PIN_MODE_DISCONNECTED``  Pin disconnected from dpll
+  ``PIN_MODE_SOURCE``        Source pin
+  ``PIN_MODE_OUTPUT``        Output pin
+
+Possible DPLL source selection mode values:
+
+  =================== ================================================
+  ``MODE_FORCED``     source pin is force-selected by
+                      ``DPLL_CMD_DEVICE_SET`` with given value of
+                      ``DPLLA_SOURCE_PIN_IDX`` attribute
+  ``MODE_AUTOMATIC``  source pin is auto selected according to
+                      configured pin priorities and source signal
+                      validity
+  ``MODE_HOLDOVER``   force holdover mode of DPLL
+  ``MODE_FREERUN``    DPLL is driven by supplied system clock without
+                      holdover capabilities
+  ``MODE_NCO``        similar to FREERUN, with possibility to
+                      numerically control frequency offset
+
+Notifications
+================
+
+DPLL device can provide notifications regarding status changes of the
+device, i.e. lock status changes, source/output type changes or alarms.
+This is the multicast group that is used to notify user-space apps via
+netlink socket:
+
+Notifications messages:
+
+  ========================= ==========================================
+  ``EVENT_DEVICE_CREATE``   event value new DPLL device was created
+    ``ID``                  attr dpll device index
+    ``NAME``                attr dpll device name
+  ``EVENT_DEVICE_DELETE``   event value DPLL device was deleted
+    ``ID``                  attr dpll device index
+  ``EVENT_DEVICE_CHANGE``   event value DPLL device attribute has changed
+    ``ID``                  attr dpll device index
+    ``CHANGE_TYPE``         attr the reason for change with values of
+                            ``enum dpll_event_change``
+
+Device change event reasons, values of ``CHANGE_TYPE`` attribute:
+
+  =========================== =========================================
+   ``CHANGE_MODE``            DPLL selection mode has changed
+   ``CHANGE_LOCK_STATUS``     DPLL lock status has changed
+   ``CHANGE_SOURCE_PIN``      DPLL source pin has changed
+   ``CHANGE_TEMP``            DPLL temperature has changed
+   ``CHANGE_PIN_ADD``         pin added to DPLL
+   ``CHANGE_PIN_DEL``         pin removed from DPLL
+   ``CHANGE_PIN_SIGNAL_TYPE`` pin signal type has changed
+   ``CHANGE_PIN_CUSTOM_FREQ`` pin custom frequency value has changed
+   ``CHANGE_PIN_MODE``        pin mode has changed
+   ``CHANGE_PIN_PRIO``        pin prio has changed
+
+
+Device driver implementation
+============================
+
+For device to operate as DPLL subsystem device, it should implement
+set of operations and register device via ``dpll_device_alloc``,
+provide the operations set, unique device clock_id, class of a clock,
+type of dpll (PPS/EEC), pointer to parent device and pointer to its
+private data, that can be used in callback ops.
+
+The pins are allocated separately with ``dpll_pin_alloc``, which
+requires providing pin description and its type.
+
+Once DPLL device is created, allocated pin can be registered with it
+with 2 different methods, always providing implemented pin callbacks,
+and private data pointer for calling them:
+``dpll_pin_register`` - simple registration with a dpll device.
+``dpll_muxed_pin_register`` - register pin with another MUX type pin.
+
+It is also possible to register pin already registered with different
+DPLL device by calling ``dpll_shared_pin_register`` - in this case
+changes requested on a single pin would affect all DPLLs which were
+registered with that pin.
+
+For different instances of a device driver requiring to find already
+registered DPLL (i.e. to connect its pins to id)
+use ``dpll_device_get_by_clock_id`` providing the same clock_id, type of
+dpll and index of the DPLL device of such type, same as given on
+original device allocation.
+
+The name od DPLL device is generated based on registerer device struct
+pointer, DPLL type and an index received from registerer device driver.
+Name is in format: ``dpll_%s_%d_%d`` witch arguments:
+``dev_name(parent)`` - syscall on parent device
+``type``             - DPLL type converted to string
+``dev_driver_idx``   - registerer given index
+
+Notifications of adding or removing DPLL devices are created within
+subsystem itself.
+Notifications about registering/deregistering pins are also invoked by
+the subsystem.
+Any other change notifications shall be requested by device driver with
+``dpll_device_notify`` or ``dpll_pin_notify`` and corresponding reason.
+Change reason enums ``dpll_event_change`` are defined in
+``<linux/dpll.h>``, constants and enums are placed in
+``<uapi/linux/dpll.h>`` to be consistent with user-space.
+
+There is no strict requirement to implement all the operations for
+each device, every operation handler is checked for existence and
+ENOTSUPP is returned in case of absence of specific handler.
+
diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
index 4f2d1f682a18..d50d98939942 100644
--- a/Documentation/networking/index.rst
+++ b/Documentation/networking/index.rst
@@ -16,6 +16,7 @@ Contents:
    device_drivers/index
    dsa/index
    devlink/index
+   dpll
    caif/index
    ethtool-netlink
    ieee802154
-- 
2.30.2


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

* [RFC PATCH v5 3/4] ice: add admin commands to access cgu configuration
  2023-01-17 18:00 ` Vadim Fedorenko
@ 2023-01-17 18:00   ` Vadim Fedorenko
  -1 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, linux-arm-kernel, linux-clk, Vadim Fedorenko

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

Add firmware admin command to access clock generation unit
configuration, it is required to enable Extended PTP and SyncE features
in the driver.
Add definitions of possible hardware variations of input and output pins
related to clock generation unit and functions to access the data.

Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
---
 drivers/net/ethernet/intel/ice/ice.h          |   1 +
 .../net/ethernet/intel/ice/ice_adminq_cmd.h   | 240 ++++++++-
 drivers/net/ethernet/intel/ice/ice_common.c   | 467 ++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_common.h   |  43 ++
 drivers/net/ethernet/intel/ice/ice_lib.c      |  17 +-
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c   | 408 +++++++++++++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   | 240 +++++++++
 drivers/net/ethernet/intel/ice/ice_type.h     |   1 +
 8 files changed, 1412 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 2f0b604abc5e..3368dba42789 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -199,6 +199,7 @@ enum ice_feature {
 	ICE_F_DSCP,
 	ICE_F_PTP_EXTTS,
 	ICE_F_SMA_CTRL,
+	ICE_F_CGU,
 	ICE_F_GNSS,
 	ICE_F_MAX
 };
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 958c1e435232..542b48a2ca92 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -1339,6 +1339,32 @@ struct ice_aqc_set_mac_lb {
 	u8 reserved[15];
 };
 
+/* Set PHY recovered clock output (direct 0x0630) */
+struct ice_aqc_set_phy_rec_clk_out {
+	u8 phy_output;
+	u8 port_num;
+#define ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT	0xFF
+	u8 flags;
+#define ICE_AQC_SET_PHY_REC_CLK_OUT_OUT_EN	BIT(0)
+	u8 rsvd;
+	__le32 freq;
+	u8 rsvd2[6];
+	__le16 node_handle;
+};
+
+/* Get PHY recovered clock output (direct 0x0631) */
+struct ice_aqc_get_phy_rec_clk_out {
+	u8 phy_output;
+	u8 port_num;
+#define ICE_AQC_GET_PHY_REC_CLK_OUT_CURR_PORT	0xFF
+	u8 flags;
+#define ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN	BIT(0)
+	u8 rsvd;
+	__le32 freq;
+	u8 rsvd2[6];
+	__le16 node_handle;
+};
+
 struct ice_aqc_link_topo_params {
 	u8 lport_num;
 	u8 lport_num_valid;
@@ -1355,6 +1381,8 @@ struct ice_aqc_link_topo_params {
 #define ICE_AQC_LINK_TOPO_NODE_TYPE_CAGE	6
 #define ICE_AQC_LINK_TOPO_NODE_TYPE_MEZZ	7
 #define ICE_AQC_LINK_TOPO_NODE_TYPE_ID_EEPROM	8
+#define ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL	9
+#define ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_MUX	10
 #define ICE_AQC_LINK_TOPO_NODE_CTX_S		4
 #define ICE_AQC_LINK_TOPO_NODE_CTX_M		\
 				(0xF << ICE_AQC_LINK_TOPO_NODE_CTX_S)
@@ -1391,7 +1419,12 @@ struct ice_aqc_link_topo_addr {
 struct ice_aqc_get_link_topo {
 	struct ice_aqc_link_topo_addr addr;
 	u8 node_part_num;
-#define ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575	0x21
+#define ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575		0x21
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032	0x24
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384	0x25
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_E822_PHY		0x30
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827		0x31
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_GEN_CLK_MUX	0x47
 	u8 rsvd[9];
 };
 
@@ -2066,6 +2099,186 @@ struct ice_aqc_get_pkg_info_resp {
 	struct ice_aqc_get_pkg_info pkg_info[];
 };
 
+/* Get CGU abilities command response data structure (indirect 0x0C61) */
+struct ice_aqc_get_cgu_abilities {
+	u8 num_inputs;
+	u8 num_outputs;
+	u8 pps_dpll_idx;
+	u8 eec_dpll_idx;
+	__le32 max_in_freq;
+	__le32 max_in_phase_adj;
+	__le32 max_out_freq;
+	__le32 max_out_phase_adj;
+	u8 cgu_part_num;
+	u8 rsvd[3];
+};
+
+/* Set CGU input config (direct 0x0C62) */
+struct ice_aqc_set_cgu_input_config {
+	u8 input_idx;
+	u8 flags1;
+#define ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ		BIT(6)
+#define ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_DELAY	BIT(7)
+	u8 flags2;
+#define ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN		BIT(5)
+#define ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN		BIT(6)
+	u8 rsvd;
+	__le32 freq;
+	__le32 phase_delay;
+	u8 rsvd2[2];
+	__le16 node_handle;
+};
+
+/* Get CGU input config response descriptor structure (direct 0x0C63) */
+struct ice_aqc_get_cgu_input_config {
+	u8 input_idx;
+	u8 status;
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_LOS		BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_SCM_FAIL		BIT(1)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_CFM_FAIL		BIT(2)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_GST_FAIL		BIT(3)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_PFM_FAIL		BIT(4)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_ESYNC_FAIL	BIT(6)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_ESYNC_CAP		BIT(7)
+	u8 type;
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_READ_ONLY		BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_GPS			BIT(4)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_EXTERNAL		BIT(5)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_PHY			BIT(6)
+	u8 flags1;
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_PHASE_DELAY_SUPP	BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_1PPS_SUPP		BIT(2)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_10MHZ_SUPP		BIT(3)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_ANYFREQ		BIT(7)
+	__le32 freq;
+	__le32 phase_delay;
+	u8 flags2;
+#define ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN		BIT(5)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN		BIT(6)
+	u8 rsvd[1];
+	__le16 node_handle;
+};
+
+/* Set CGU output config (direct 0x0C64) */
+struct ice_aqc_set_cgu_output_config {
+	u8 output_idx;
+	u8 flags;
+#define ICE_AQC_SET_CGU_OUT_CFG_OUT_EN		BIT(0)
+#define ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN	BIT(1)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ     BIT(2)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_PHASE    BIT(3)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_SRC_SEL  BIT(4)
+	u8 src_sel;
+#define ICE_AQC_SET_CGU_OUT_CFG_DPLL_SRC_SEL    ICE_M(0x1F, 0)
+	u8 rsvd;
+	__le32 freq;
+	__le32 phase_delay;
+	u8 rsvd2[2];
+	__le16 node_handle;
+};
+
+/* Get CGU output config (direct 0x0C65) */
+struct ice_aqc_get_cgu_output_config {
+	u8 output_idx;
+	u8 flags;
+#define ICE_AQC_GET_CGU_OUT_CFG_OUT_EN		BIT(0)
+#define ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN	BIT(1)
+#define ICE_AQC_GET_CGU_OUT_CFG_ESYNC_ABILITY	BIT(2)
+	u8 src_sel;
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL_SHIFT	0
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL \
+	ICE_M(0x1F, ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL_SHIFT)
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE_SHIFT		5
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE \
+	ICE_M(0x7, ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE_SHIFT)
+	u8 rsvd;
+	__le32 freq;
+	__le32 src_freq;
+	u8 rsvd2[2];
+	__le16 node_handle;
+};
+
+/* Get CGU DPLL status (direct 0x0C66) */
+struct ice_aqc_get_cgu_dpll_status {
+	u8 dpll_num;
+	u8 ref_state;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_LOS		BIT(0)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_SCM		BIT(1)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_CFM		BIT(2)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_GST		BIT(3)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_PFM		BIT(4)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_FAST_LOCK_EN	BIT(5)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_ESYNC	BIT(6)
+	__le16 dpll_state;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_LOCK		BIT(0)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO		BIT(1)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO_READY	BIT(2)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_FLHIT		BIT(5)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_PSLHIT	BIT(7)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT	8
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SEL	\
+	ICE_M(0x1F, ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE_SHIFT	13
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE \
+	ICE_M(0x7, ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE_SHIFT)
+	__le32 phase_offset_h;
+	__le32 phase_offset_l;
+	u8 eec_mode;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_1		0xA
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_2		0xB
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_UNKNOWN	0xF
+	u8 rsvd[1];
+	__le16 node_handle;
+};
+
+/* Set CGU DPLL config (direct 0x0C67) */
+struct ice_aqc_set_cgu_dpll_config {
+	u8 dpll_num;
+	u8 ref_state;
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_LOS		BIT(0)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_SCM		BIT(1)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_CFM		BIT(2)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_GST		BIT(3)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_PFM		BIT(4)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_FLOCK_EN	BIT(5)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_ESYNC	BIT(6)
+	u8 rsvd;
+	u8 config;
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_CLK_REF_SEL		ICE_M(0x1F, 0)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE		ICE_M(0x7, 5)
+	u8 rsvd2[8];
+	u8 eec_mode;
+	u8 rsvd3[1];
+	__le16 node_handle;
+};
+
+/* Set CGU reference priority (direct 0x0C68) */
+struct ice_aqc_set_cgu_ref_prio {
+	u8 dpll_num;
+	u8 ref_idx;
+	u8 ref_priority;
+	u8 rsvd[11];
+	__le16 node_handle;
+};
+
+/* Get CGU reference priority (direct 0x0C69) */
+struct ice_aqc_get_cgu_ref_prio {
+	u8 dpll_num;
+	u8 ref_idx;
+	u8 ref_priority; /* Valid only in response */
+	u8 rsvd[13];
+};
+
+/* Get CGU info (direct 0x0C6A) */
+struct ice_aqc_get_cgu_info {
+	__le32 cgu_id;
+	__le32 cgu_cfg_ver;
+	__le32 cgu_fw_ver;
+	u8 node_part_num;
+	u8 dev_rev;
+	__le16 node_handle;
+};
+
 /* Driver Shared Parameters (direct, 0x0C90) */
 struct ice_aqc_driver_shared_params {
 	u8 set_or_get_op;
@@ -2135,6 +2348,8 @@ struct ice_aq_desc {
 		struct ice_aqc_get_phy_caps get_phy;
 		struct ice_aqc_set_phy_cfg set_phy;
 		struct ice_aqc_restart_an restart_an;
+		struct ice_aqc_set_phy_rec_clk_out set_phy_rec_clk_out;
+		struct ice_aqc_get_phy_rec_clk_out get_phy_rec_clk_out;
 		struct ice_aqc_gpio read_write_gpio;
 		struct ice_aqc_sff_eeprom read_write_sff_param;
 		struct ice_aqc_set_port_id_led set_port_id_led;
@@ -2174,6 +2389,15 @@ struct ice_aq_desc {
 		struct ice_aqc_fw_logging fw_logging;
 		struct ice_aqc_get_clear_fw_log get_clear_fw_log;
 		struct ice_aqc_download_pkg download_pkg;
+		struct ice_aqc_set_cgu_input_config set_cgu_input_config;
+		struct ice_aqc_get_cgu_input_config get_cgu_input_config;
+		struct ice_aqc_set_cgu_output_config set_cgu_output_config;
+		struct ice_aqc_get_cgu_output_config get_cgu_output_config;
+		struct ice_aqc_get_cgu_dpll_status get_cgu_dpll_status;
+		struct ice_aqc_set_cgu_dpll_config set_cgu_dpll_config;
+		struct ice_aqc_set_cgu_ref_prio set_cgu_ref_prio;
+		struct ice_aqc_get_cgu_ref_prio get_cgu_ref_prio;
+		struct ice_aqc_get_cgu_info get_cgu_info;
 		struct ice_aqc_driver_shared_params drv_shared_params;
 		struct ice_aqc_set_mac_lb set_mac_lb;
 		struct ice_aqc_alloc_free_res_cmd sw_res_ctrl;
@@ -2297,6 +2521,8 @@ enum ice_adminq_opc {
 	ice_aqc_opc_get_link_status			= 0x0607,
 	ice_aqc_opc_set_event_mask			= 0x0613,
 	ice_aqc_opc_set_mac_lb				= 0x0620,
+	ice_aqc_opc_set_phy_rec_clk_out			= 0x0630,
+	ice_aqc_opc_get_phy_rec_clk_out			= 0x0631,
 	ice_aqc_opc_get_link_topo			= 0x06E0,
 	ice_aqc_opc_read_i2c				= 0x06E2,
 	ice_aqc_opc_write_i2c				= 0x06E3,
@@ -2350,6 +2576,18 @@ enum ice_adminq_opc {
 	ice_aqc_opc_update_pkg				= 0x0C42,
 	ice_aqc_opc_get_pkg_info_list			= 0x0C43,
 
+	/* 1588/SyncE commands/events */
+	ice_aqc_opc_get_cgu_abilities			= 0x0C61,
+	ice_aqc_opc_set_cgu_input_config		= 0x0C62,
+	ice_aqc_opc_get_cgu_input_config		= 0x0C63,
+	ice_aqc_opc_set_cgu_output_config		= 0x0C64,
+	ice_aqc_opc_get_cgu_output_config		= 0x0C65,
+	ice_aqc_opc_get_cgu_dpll_status			= 0x0C66,
+	ice_aqc_opc_set_cgu_dpll_config			= 0x0C67,
+	ice_aqc_opc_set_cgu_ref_prio			= 0x0C68,
+	ice_aqc_opc_get_cgu_ref_prio			= 0x0C69,
+	ice_aqc_opc_get_cgu_info			= 0x0C6A,
+
 	ice_aqc_opc_driver_shared_params		= 0x0C90,
 
 	/* Standalone Commands/Events */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index d02b55b6aa9c..67c8934ee8f9 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -409,6 +409,83 @@ ice_aq_get_link_topo_handle(struct ice_port_info *pi, u8 node_type,
 	return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd);
 }
 
+/**
+ * ice_aq_get_netlist_node
+ * @hw: pointer to the hw struct
+ * @cmd: get_link_topo AQ structure
+ * @node_part_number: output node part number if node found
+ * @node_handle: output node handle parameter if node found
+ *
+ * Get netlist node handle.
+ */
+int
+ice_aq_get_netlist_node(struct ice_hw *hw, struct ice_aqc_get_link_topo *cmd,
+			u8 *node_part_number, u16 *node_handle)
+{
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_link_topo);
+	desc.params.get_link_topo = *cmd;
+
+	if (ice_aq_send_cmd(hw, &desc, NULL, 0, NULL))
+		return -EINTR;
+
+	if (node_handle)
+		*node_handle =
+			le16_to_cpu(desc.params.get_link_topo.addr.handle);
+	if (node_part_number)
+		*node_part_number = desc.params.get_link_topo.node_part_num;
+
+	return 0;
+}
+
+#define MAX_NETLIST_SIZE	10
+
+/**
+ * ice_find_netlist_node
+ * @hw: pointer to the hw struct
+ * @node_type_ctx: type of netlist node to look for
+ * @node_part_number: node part number to look for
+ * @node_handle: output parameter if node found - optional
+ *
+ * Find and return the node handle for a given node type and part number in the
+ * netlist. When found ICE_SUCCESS is returned, ICE_ERR_DOES_NOT_EXIST
+ * otherwise. If node_handle provided, it would be set to found node handle.
+ */
+int
+ice_find_netlist_node(struct ice_hw *hw, u8 node_type_ctx, u8 node_part_number,
+		      u16 *node_handle)
+{
+	struct ice_aqc_get_link_topo cmd;
+	u8 rec_node_part_number;
+	u16 rec_node_handle;
+	u8 idx;
+
+	for (idx = 0; idx < MAX_NETLIST_SIZE; idx++) {
+		int status;
+
+		memset(&cmd, 0, sizeof(cmd));
+
+		cmd.addr.topo_params.node_type_ctx =
+			(node_type_ctx << ICE_AQC_LINK_TOPO_NODE_TYPE_S);
+		cmd.addr.topo_params.index = idx;
+
+		status = ice_aq_get_netlist_node(hw, &cmd,
+						 &rec_node_part_number,
+						 &rec_node_handle);
+		if (status)
+			return status;
+
+		if (rec_node_part_number == node_part_number) {
+			if (node_handle)
+				*node_handle = rec_node_handle;
+			return 0;
+		}
+	}
+
+	return -ENOTBLK;
+}
+
 /**
  * ice_is_media_cage_present
  * @pi: port information structure
@@ -4904,6 +4981,396 @@ ice_dis_vsi_rdma_qset(struct ice_port_info *pi, u16 count, u32 *qset_teid,
 	return status;
 }
 
+/**
+ * ice_aq_get_cgu_abilities
+ * @hw: pointer to the HW struct
+ * @abilities: CGU abilities
+ *
+ * Get CGU abilities (0x0C61)
+ */
+int
+ice_aq_get_cgu_abilities(struct ice_hw *hw,
+			 struct ice_aqc_get_cgu_abilities *abilities)
+{
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_abilities);
+	return ice_aq_send_cmd(hw, &desc, abilities, sizeof(*abilities), NULL);
+}
+
+/**
+ * ice_aq_set_input_pin_cfg
+ * @hw: pointer to the HW struct
+ * @input_idx: Input index
+ * @flags1: Input flags
+ * @flags2: Input flags
+ * @freq: Frequency in Hz
+ * @phase_delay: Delay in ps
+ *
+ * Set CGU input config (0x0C62)
+ */
+int
+ice_aq_set_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 flags1, u8 flags2,
+			 u32 freq, s32 phase_delay)
+{
+	struct ice_aqc_set_cgu_input_config *cmd;
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_input_config);
+	cmd = &desc.params.set_cgu_input_config;
+	cmd->input_idx = input_idx;
+	cmd->flags1 = flags1;
+	cmd->flags2 = flags2;
+	cmd->freq = cpu_to_le32(freq);
+	cmd->phase_delay = cpu_to_le32(phase_delay);
+
+	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_input_pin_cfg
+ * @hw: pointer to the HW struct
+ * @input_idx: Input index
+ * @status: Pin status
+ * @type: Pin type
+ * @flags1: Input flags
+ * @flags2: Input flags
+ * @freq: Frequency in Hz
+ * @phase_delay: Delay in ps
+ *
+ * Get CGU input config (0x0C63)
+ */
+int
+ice_aq_get_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 *status, u8 *type,
+			 u8 *flags1, u8 *flags2, u32 *freq, s32 *phase_delay)
+{
+	struct ice_aqc_get_cgu_input_config *cmd;
+	struct ice_aq_desc desc;
+	int ret;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_input_config);
+	cmd = &desc.params.get_cgu_input_config;
+	cmd->input_idx = input_idx;
+
+	ret = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!ret) {
+		if (status)
+			*status = cmd->status;
+		if (type)
+			*type = cmd->type;
+		if (flags1)
+			*flags1 = cmd->flags1;
+		if (flags2)
+			*flags2 = cmd->flags2;
+		if (freq)
+			*freq = le32_to_cpu(cmd->freq);
+		if (phase_delay)
+			*phase_delay = le32_to_cpu(cmd->phase_delay);
+	}
+
+	return ret;
+}
+
+/**
+ * ice_aq_set_output_pin_cfg
+ * @hw: pointer to the HW struct
+ * @output_idx: Output index
+ * @flags: Output flags
+ * @src_sel: Index of DPLL block
+ * @freq: Output frequency
+ * @phase_delay: Output phase compensation
+ *
+ * Set CGU output config (0x0C64)
+ */
+int
+ice_aq_set_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 flags,
+			  u8 src_sel, u32 freq, s32 phase_delay)
+{
+	struct ice_aqc_set_cgu_output_config *cmd;
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_output_config);
+	cmd = &desc.params.set_cgu_output_config;
+	cmd->output_idx = output_idx;
+	cmd->flags = flags;
+	cmd->src_sel = src_sel;
+	cmd->freq = cpu_to_le32(freq);
+	cmd->phase_delay = cpu_to_le32(phase_delay);
+
+	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_output_pin_cfg
+ * @hw: pointer to the HW struct
+ * @output_idx: Output index
+ * @flags: Output flags
+ * @src_sel: Internal DPLL source
+ * @freq: Output frequency
+ * @src_freq: Source frequency
+ *
+ * Get CGU output config (0x0C65)
+ */
+int
+ice_aq_get_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 *flags,
+			  u8 *src_sel, u32 *freq, u32 *src_freq)
+{
+	struct ice_aqc_get_cgu_output_config *cmd;
+	struct ice_aq_desc desc;
+	int ret;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_output_config);
+	cmd = &desc.params.get_cgu_output_config;
+	cmd->output_idx = output_idx;
+
+	ret = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!ret) {
+		if (flags)
+			*flags = cmd->flags;
+		if (src_sel)
+			*src_sel = cmd->src_sel;
+		if (freq)
+			*freq = le32_to_cpu(cmd->freq);
+		if (src_freq)
+			*src_freq = le32_to_cpu(cmd->src_freq);
+	}
+
+	return ret;
+}
+
+/**
+ * convert_s48_to_s64 - convert 48 bit value to 64 bit value
+ * @signed_48: signed 64 bit variable storing signed 48 bit value
+ *
+ * Convert signed 48 bit value to its 64 bit representation.
+ *
+ * Return: signed 64 bit representation of signed 48 bit value.
+ */
+static inline
+s64 convert_s48_to_s64(s64 signed_48)
+{
+	const s64 MASK_SIGN_BITS = GENMASK_ULL(63, 48);
+	const s64 SIGN_BIT_47 = BIT_ULL(47);
+
+	return ((signed_48 & SIGN_BIT_47) ? (s64)(MASK_SIGN_BITS | signed_48)
+		: signed_48);
+}
+
+/**
+ * ice_aq_get_cgu_dpll_status
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_state: Reference clock state
+ * @dpll_state: DPLL state
+ * @phase_offset: Phase offset in ns
+ * @eec_mode: EEC_mode
+ *
+ * Get CGU DPLL status (0x0C66)
+ */
+int
+ice_aq_get_cgu_dpll_status(struct ice_hw *hw, u8 dpll_num, u8 *ref_state,
+			   u16 *dpll_state, s64 *phase_offset, u8 *eec_mode)
+{
+	struct ice_aqc_get_cgu_dpll_status *cmd;
+	const s64 NSEC_PER_PSEC = 1000LL;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_dpll_status);
+	cmd = &desc.params.get_cgu_dpll_status;
+	cmd->dpll_num = dpll_num;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status) {
+		*ref_state = cmd->ref_state;
+		*dpll_state = le16_to_cpu(cmd->dpll_state);
+		*phase_offset = le32_to_cpu(cmd->phase_offset_h);
+		*phase_offset <<= 32;
+		*phase_offset += le32_to_cpu(cmd->phase_offset_l);
+		*phase_offset = convert_s48_to_s64(*phase_offset)
+				/ NSEC_PER_PSEC;
+		*eec_mode = cmd->eec_mode;
+	}
+
+	return status;
+}
+
+/**
+ * ice_aq_set_cgu_dpll_config
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_state: Reference clock state
+ * @config: DPLL config
+ * @eec_mode: EEC mode
+ *
+ * Set CGU DPLL config (0x0C67)
+ */
+int
+ice_aq_set_cgu_dpll_config(struct ice_hw *hw, u8 dpll_num, u8 ref_state,
+			   u8 config, u8 eec_mode)
+{
+	struct ice_aqc_set_cgu_dpll_config *cmd;
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_dpll_config);
+	cmd = &desc.params.set_cgu_dpll_config;
+	cmd->dpll_num = dpll_num;
+	cmd->ref_state = ref_state;
+	cmd->config = config;
+	cmd->eec_mode = eec_mode;
+
+	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_set_cgu_ref_prio
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_idx: Reference pin index
+ * @ref_priority: Reference input priority
+ *
+ * Set CGU reference priority (0x0C68)
+ */
+int
+ice_aq_set_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+			u8 ref_priority)
+{
+	struct ice_aqc_set_cgu_ref_prio *cmd;
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_ref_prio);
+	cmd = &desc.params.set_cgu_ref_prio;
+	cmd->dpll_num = dpll_num;
+	cmd->ref_idx = ref_idx;
+	cmd->ref_priority = ref_priority;
+
+	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_cgu_ref_prio
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_idx: Reference pin index
+ * @ref_prio: Reference input priority
+ *
+ * Get CGU reference priority (0x0C69)
+ */
+int
+ice_aq_get_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+			u8 *ref_prio)
+{
+	struct ice_aqc_get_cgu_ref_prio *cmd;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_ref_prio);
+	cmd = &desc.params.get_cgu_ref_prio;
+	cmd->dpll_num = dpll_num;
+	cmd->ref_idx = ref_idx;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status)
+		*ref_prio = cmd->ref_priority;
+
+	return status;
+}
+
+/**
+ * ice_aq_get_cgu_info
+ * @hw: pointer to the HW struct
+ * @cgu_id: CGU ID
+ * @cgu_cfg_ver: CGU config version
+ * @cgu_fw_ver: CGU firmware version
+ *
+ * Get CGU info (0x0C6A)
+ */
+int
+ice_aq_get_cgu_info(struct ice_hw *hw, u32 *cgu_id, u32 *cgu_cfg_ver,
+		    u32 *cgu_fw_ver)
+{
+	struct ice_aqc_get_cgu_info *cmd;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_info);
+	cmd = &desc.params.get_cgu_info;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status) {
+		*cgu_id = le32_to_cpu(cmd->cgu_id);
+		*cgu_cfg_ver = le32_to_cpu(cmd->cgu_cfg_ver);
+		*cgu_fw_ver = le32_to_cpu(cmd->cgu_fw_ver);
+	}
+
+	return status;
+}
+
+/**
+ * ice_aq_set_phy_rec_clk_out - set RCLK phy out
+ * @hw: pointer to the HW struct
+ * @phy_output: PHY reference clock output pin
+ * @enable: GPIO state to be applied
+ * @freq: PHY output frequency
+ *
+ * Set CGU reference priority (0x0630)
+ * Return 0 on success or negative value on failure.
+ */
+int
+ice_aq_set_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, bool enable,
+			   u32 *freq)
+{
+	struct ice_aqc_set_phy_rec_clk_out *cmd;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_phy_rec_clk_out);
+	cmd = &desc.params.set_phy_rec_clk_out;
+	cmd->phy_output = phy_output;
+	cmd->port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
+	cmd->flags = enable & ICE_AQC_SET_PHY_REC_CLK_OUT_OUT_EN;
+	cmd->freq = cpu_to_le32(*freq);
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status)
+		*freq = le32_to_cpu(cmd->freq);
+
+	return status;
+}
+
+/**
+ * ice_aq_get_phy_rec_clk_out
+ * @hw: pointer to the HW struct
+ * @phy_output: PHY reference clock output pin
+ * @port_num: Port number
+ * @flags: PHY flags
+ * @freq: PHY output frequency
+ *
+ * Get PHY recovered clock output (0x0631)
+ */
+int
+ice_aq_get_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, u8 *port_num,
+			   u8 *flags, u32 *freq)
+{
+	struct ice_aqc_get_phy_rec_clk_out *cmd;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_phy_rec_clk_out);
+	cmd = &desc.params.get_phy_rec_clk_out;
+	cmd->phy_output = phy_output;
+	cmd->port_num = *port_num;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status) {
+		*port_num = cmd->port_num;
+		*flags = cmd->flags;
+		*freq = le32_to_cpu(cmd->freq);
+	}
+
+	return status;
+}
+
 /**
  * ice_replay_pre_init - replay pre initialization
  * @hw: pointer to the HW struct
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
index 4c6a0b5c9304..637c3e5eea8d 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.h
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -94,6 +94,12 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
 		    struct ice_aqc_get_phy_caps_data *caps,
 		    struct ice_sq_cd *cd);
 int
+ice_find_netlist_node(struct ice_hw *hw, u8 node_type_ctx, u8 node_part_number,
+		      u16 *node_handle);
+int
+ice_aq_get_netlist_node(struct ice_hw *hw, struct ice_aqc_get_link_topo *cmd,
+			u8 *node_part_number, u16 *node_handle);
+int
 ice_aq_list_caps(struct ice_hw *hw, void *buf, u16 buf_size, u32 *cap_count,
 		 enum ice_adminq_opc opc, struct ice_sq_cd *cd);
 int
@@ -192,6 +198,43 @@ void ice_output_fw_log(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf);
 struct ice_q_ctx *
 ice_get_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 q_handle);
 int ice_sbq_rw_reg(struct ice_hw *hw, struct ice_sbq_msg_input *in);
+int
+ice_aq_get_cgu_abilities(struct ice_hw *hw,
+			 struct ice_aqc_get_cgu_abilities *abilities);
+int
+ice_aq_set_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 flags1, u8 flags2,
+			 u32 freq, s32 phase_delay);
+int
+ice_aq_get_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 *status, u8 *type,
+			 u8 *flags1, u8 *flags2, u32 *freq, s32 *phase_delay);
+int
+ice_aq_set_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 flags,
+			  u8 src_sel, u32 freq, s32 phase_delay);
+int
+ice_aq_get_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 *flags,
+			  u8 *src_sel, u32 *freq, u32 *src_freq);
+int
+ice_aq_get_cgu_dpll_status(struct ice_hw *hw, u8 dpll_num, u8 *ref_state,
+			   u16 *dpll_state, s64 *phase_offset, u8 *eec_mode);
+int
+ice_aq_set_cgu_dpll_config(struct ice_hw *hw, u8 dpll_num, u8 ref_state,
+			   u8 config, u8 eec_mode);
+int
+ice_aq_set_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+			u8 ref_priority);
+int
+ice_aq_get_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+			u8 *ref_prio);
+int
+ice_aq_get_cgu_info(struct ice_hw *hw, u32 *cgu_id, u32 *cgu_cfg_ver,
+		    u32 *cgu_fw_ver);
+
+int
+ice_aq_set_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, bool enable,
+			   u32 *freq);
+int
+ice_aq_get_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, u8 *port_num,
+			   u8 *flags, u32 *freq);
 void
 ice_stat_update40(struct ice_hw *hw, u32 reg, bool prev_stat_loaded,
 		  u64 *prev_stat, u64 *cur_stat);
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 94aa834cd9a6..6d965e030772 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4426,13 +4426,22 @@ void ice_init_feature_support(struct ice_pf *pf)
 	case ICE_DEV_ID_E810C_BACKPLANE:
 	case ICE_DEV_ID_E810C_QSFP:
 	case ICE_DEV_ID_E810C_SFP:
+	case ICE_DEV_ID_E810_XXV_BACKPLANE:
+	case ICE_DEV_ID_E810_XXV_QSFP:
+	case ICE_DEV_ID_E810_XXV_SFP:
 		ice_set_feature_support(pf, ICE_F_DSCP);
 		ice_set_feature_support(pf, ICE_F_PTP_EXTTS);
-		if (ice_is_e810t(&pf->hw)) {
+		if (ice_is_phy_rclk_present(&pf->hw))
+			ice_set_feature_support(pf, ICE_F_PHY_RCLK);
+		/* If we don't own the timer - don't enable other caps */
+		if (!pf->hw.func_caps.ts_func_info.src_tmr_owned)
+			break;
+		if (ice_is_cgu_present(&pf->hw))
+			ice_set_feature_support(pf, ICE_F_CGU);
+		if (ice_is_clock_mux_present_e810t(&pf->hw))
 			ice_set_feature_support(pf, ICE_F_SMA_CTRL);
-			if (ice_gnss_is_gps_present(&pf->hw))
-				ice_set_feature_support(pf, ICE_F_GNSS);
-		}
+		if (ice_gnss_is_gps_present(&pf->hw))
+			ice_set_feature_support(pf, ICE_F_GNSS);
 		break;
 	default:
 		break;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index a38614d21ea8..01bf15941f85 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -3213,6 +3213,91 @@ ice_get_pca9575_handle(struct ice_hw *hw, u16 *pca9575_handle)
 	return 0;
 }
 
+/**
+ * ice_is_phy_rclk_present
+ * @hw: pointer to the hw struct
+ *
+ * Check if the PHY Recovered Clock device is present in the netlist
+ * Return:
+ * * true - device found in netlist
+ * * false - device not found
+ */
+bool ice_is_phy_rclk_present(struct ice_hw *hw)
+{
+	if (ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+				  ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827, NULL) &&
+	    ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+				  ICE_ACQ_GET_LINK_TOPO_NODE_NR_E822_PHY, NULL))
+		return false;
+
+	return true;
+}
+
+/**
+ * ice_is_clock_mux_present_e810t
+ * @hw: pointer to the hw struct
+ *
+ * Check if the Clock Multiplexer device is present in the netlist
+ * Return:
+ * * true - device found in netlist
+ * * false - device not found
+ */
+bool ice_is_clock_mux_present_e810t(struct ice_hw *hw)
+{
+	if (ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_MUX,
+				  ICE_ACQ_GET_LINK_TOPO_NODE_NR_GEN_CLK_MUX,
+				  NULL))
+		return false;
+
+	return true;
+}
+
+/**
+ * ice_get_pf_c827_idx - find and return the C827 index for the current pf
+ * @hw: pointer to the hw struct
+ * @idx: index of the found C827 PHY
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx)
+{
+	struct ice_aqc_get_link_topo cmd;
+	u8 node_part_number;
+	u16 node_handle;
+	int status;
+	u8 ctx;
+
+	if (hw->mac_type != ICE_MAC_E810)
+		return -ENODEV;
+
+	if (hw->device_id != ICE_DEV_ID_E810C_QSFP) {
+		*idx = C827_0;
+		return 0;
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctx = ICE_AQC_LINK_TOPO_NODE_TYPE_PHY << ICE_AQC_LINK_TOPO_NODE_TYPE_S;
+	ctx |= ICE_AQC_LINK_TOPO_NODE_CTX_PORT << ICE_AQC_LINK_TOPO_NODE_CTX_S;
+	cmd.addr.topo_params.node_type_ctx = ctx;
+	cmd.addr.topo_params.index = 0;
+
+	status = ice_aq_get_netlist_node(hw, &cmd, &node_part_number,
+					 &node_handle);
+	if (status || node_part_number != ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827)
+		return -ENOENT;
+
+	if (node_handle == E810C_QSFP_C827_0_HANDLE)
+		*idx = C827_0;
+	else if (node_handle == E810C_QSFP_C827_1_HANDLE)
+		*idx = C827_1;
+	else
+		return -EIO;
+
+	return 0;
+}
+
 /**
  * ice_read_sma_ctrl_e810t
  * @hw: pointer to the hw struct
@@ -3381,3 +3466,326 @@ int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready)
 		return ice_get_phy_tx_tstamp_ready_e822(hw, block,
 							tstamp_ready);
 }
+
+/**
+ * ice_is_cgu_present
+ * @hw: pointer to the hw struct
+ *
+ * Check if the Clock Generation Unit (CGU) device is present in the netlist
+ * Return:
+ * * true - cgu is present
+ * * false - cgu is not present
+ */
+bool ice_is_cgu_present(struct ice_hw *hw)
+{
+	if (!ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+				   ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032,
+				   NULL)) {
+		hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032;
+		return true;
+	} else if (!ice_find_netlist_node(hw,
+					  ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+					  ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384,
+					  NULL)) {
+		hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384;
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * ice_cgu_get_pin_desc_e823
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pin
+ * @size: number of inputs/outputs
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc_e823(struct ice_hw *hw, bool input, int *size)
+{
+	static const struct ice_cgu_pin_desc *t;
+
+	if (hw->cgu_part_number ==
+	    ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032) {
+		if (input) {
+			t = ice_e823_zl_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e823_zl_cgu_inputs);
+		} else {
+			t = ice_e823_zl_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e823_zl_cgu_outputs);
+		}
+	} else if (hw->cgu_part_number ==
+		   ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384) {
+		if (input) {
+			t = ice_e823_si_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e823_si_cgu_inputs);
+		} else {
+			t = ice_e823_si_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e823_si_cgu_outputs);
+		}
+	} else {
+		t = NULL;
+		*size = 0;
+	}
+
+	return t;
+}
+
+/**
+ * ice_cgu_get_pin_desc
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pins
+ * @size: size of array returned by function
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc(struct ice_hw *hw, bool input, int *size)
+{
+	const struct ice_cgu_pin_desc *t = NULL;
+
+	switch (hw->device_id) {
+	case ICE_DEV_ID_E810C_SFP:
+		if (input) {
+			t = ice_e810t_sfp_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e810t_sfp_cgu_inputs);
+		} else {
+			t = ice_e810t_sfp_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e810t_sfp_cgu_outputs);
+		}
+		break;
+	case ICE_DEV_ID_E810C_QSFP:
+		if (input) {
+			t = ice_e810t_qsfp_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e810t_qsfp_cgu_inputs);
+		} else {
+			t = ice_e810t_qsfp_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e810t_qsfp_cgu_outputs);
+		}
+		break;
+	case ICE_DEV_ID_E823L_10G_BASE_T:
+	case ICE_DEV_ID_E823L_1GBE:
+	case ICE_DEV_ID_E823L_BACKPLANE:
+	case ICE_DEV_ID_E823L_QSFP:
+	case ICE_DEV_ID_E823L_SFP:
+	case ICE_DEV_ID_E823C_10G_BASE_T:
+	case ICE_DEV_ID_E823C_BACKPLANE:
+	case ICE_DEV_ID_E823C_QSFP:
+	case ICE_DEV_ID_E823C_SFP:
+	case ICE_DEV_ID_E823C_SGMII:
+		t = ice_cgu_get_pin_desc_e823(hw, input, size);
+		break;
+	default:
+		break;
+	}
+
+	return t;
+}
+
+/**
+ * ice_cgu_get_pin_type
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: type of a pin.
+ */
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return 0;
+
+	if (pin >= t_size)
+		return 0;
+
+	return t[pin].type;
+}
+
+/**
+ * ice_cgu_get_pin_sig_type_mask
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: signal type bit mask of a pin.
+ */
+unsigned long
+ice_cgu_get_pin_sig_type_mask(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return 0;
+
+	if (pin >= t_size)
+		return 0;
+
+	return t[pin].sig_type_mask;
+}
+
+/**
+ * ice_cgu_get_pin_name
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return:
+ * * null terminated char array with name
+ * * NULL in case of failure
+ */
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return NULL;
+
+	if (pin >= t_size)
+		return NULL;
+
+	return t[pin].name;
+}
+
+/**
+ * ice_get_cgu_state - get the state of the DPLL
+ * @hw: pointer to the hw struct
+ * @dpll_idx: Index of internal DPLL unit
+ * @last_dpll_state: last known state of DPLL
+ * @pin: pointer to a buffer for returning currently active pin
+ * @ref_state: reference clock state
+ * @phase_offset: pointer to a buffer for returning phase offset
+ * @dpll_state: state of the DPLL (output)
+ *
+ * This function will read the state of the DPLL(dpll_idx). Non-null
+ * 'pin', 'ref_state', 'eec_mode' and 'phase_offset' parameters are used to
+ * retrieve currently active pin, state, mode and phase_offset respectively.
+ *
+ * Return: state of the DPLL
+ */
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+		      enum ice_cgu_state last_dpll_state, u8 *pin,
+		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+		      enum ice_cgu_state *dpll_state)
+{
+	u8 hw_ref_state, hw_eec_mode;
+	s64 hw_phase_offset;
+	u16 hw_dpll_state;
+	int status;
+
+	status = ice_aq_get_cgu_dpll_status(hw, dpll_idx, &hw_ref_state,
+					    &hw_dpll_state, &hw_phase_offset,
+					    &hw_eec_mode);
+	if (status) {
+		*dpll_state = ICE_CGU_STATE_INVALID;
+		return status;
+	}
+
+	if (pin) {
+		/* current ref pin in dpll_state_refsel_status_X register */
+		*pin = (hw_dpll_state &
+			ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SEL) >>
+		       ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT;
+	}
+
+	if (phase_offset)
+		*phase_offset = hw_phase_offset;
+
+	if (ref_state)
+		*ref_state = hw_ref_state;
+
+	if (eec_mode)
+		*eec_mode = hw_eec_mode;
+
+	if (!dpll_state)
+		return status;
+
+	/* According to ZL DPLL documentation, once state reach LOCKED_HO_ACQ
+	 * it would never return to FREERUN. This aligns to ITU-T G.781
+	 * Recommendation. We cannot report HOLDOVER as HO memory is cleared
+	 * while switching to another reference.
+	 * Only for situations where previous state was either: "LOCKED without
+	 * HO_ACQ" or "HOLDOVER" we actually back to FREERUN.
+	 */
+	if (hw_dpll_state & ICE_AQC_GET_CGU_DPLL_STATUS_STATE_LOCK) {
+		if (hw_dpll_state & ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO_READY)
+			*dpll_state = ICE_CGU_STATE_LOCKED_HO_ACQ;
+		else
+			*dpll_state = ICE_CGU_STATE_LOCKED;
+	} else if (last_dpll_state == ICE_CGU_STATE_LOCKED_HO_ACQ ||
+		   last_dpll_state == ICE_CGU_STATE_HOLDOVER) {
+		*dpll_state = ICE_CGU_STATE_HOLDOVER;
+	}
+	return status;
+}
+
+/**
+ * ice_get_cgu_rclk_pin_info - get info on available recovered clock pins
+ * @hw: pointer to the hw struct
+ * @base_idx: returns index of first recovered clock pin on device
+ * @pin_num: returns number of recovered clock pins available on device
+ *
+ * Based on hw provide caller info about recovery clock pins available on the
+ * board.
+ *
+ * Return:
+ * * 0 - success, information is valid
+ * * negative - failure, information is not valid
+ */
+int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num)
+{
+	int ret;
+
+	switch (hw->device_id) {
+	case ICE_DEV_ID_E810C_SFP:
+	case ICE_DEV_ID_E810C_QSFP:
+		u8 phy_idx;
+
+		ret = ice_get_pf_c827_idx(hw, &phy_idx);
+		if (ret)
+			return ret;
+		*base_idx = E810T_CGU_INPUT_C827(phy_idx, ICE_RCLKA_PIN);
+		*pin_num = ICE_E810_RCLK_PINS_NUM;
+		ret = 0;
+		break;
+	case ICE_DEV_ID_E823L_10G_BASE_T:
+	case ICE_DEV_ID_E823L_1GBE:
+	case ICE_DEV_ID_E823L_BACKPLANE:
+	case ICE_DEV_ID_E823L_QSFP:
+	case ICE_DEV_ID_E823L_SFP:
+	case ICE_DEV_ID_E823C_10G_BASE_T:
+	case ICE_DEV_ID_E823C_BACKPLANE:
+	case ICE_DEV_ID_E823C_QSFP:
+	case ICE_DEV_ID_E823C_SFP:
+	case ICE_DEV_ID_E823C_SGMII:
+		*pin_num = ICE_E822_RCLK_PINS_NUM;
+		ret = 0;
+		if (hw->cgu_part_number ==
+		    ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032)
+			*base_idx = ZL_REF1P;
+		else if (hw->cgu_part_number ==
+			 ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384)
+			*base_idx = SI_REF1P;
+		else
+			ret = -ENODEV;
+
+		break;
+	default:
+		ret = -ENODEV;
+		break;
+	}
+
+	return ret;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index 3b68cb91bd81..855880f026fc 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -3,6 +3,7 @@
 
 #ifndef _ICE_PTP_HW_H_
 #define _ICE_PTP_HW_H_
+#include <uapi/linux/dpll.h>
 
 enum ice_ptp_tmr_cmd {
 	INIT_TIME,
@@ -109,6 +110,232 @@ struct ice_cgu_pll_params_e822 {
 	u32 post_pll_div;
 };
 
+#define E810C_QSFP_C827_0_HANDLE	2
+#define E810C_QSFP_C827_1_HANDLE	3
+enum ice_e810_c827_idx {
+	C827_0,
+	C827_1
+};
+
+enum ice_phy_rclk_pins {
+	ICE_RCLKA_PIN = 0,		/* SCL pin */
+	ICE_RCLKB_PIN,			/* SDA pin */
+};
+
+#define ICE_E810_RCLK_PINS_NUM		(ICE_RCLKB_PIN + 1)
+#define ICE_E822_RCLK_PINS_NUM		(ICE_RCLKA_PIN + 1)
+#define E810T_CGU_INPUT_C827(_phy, _pin) ((_phy) * ICE_E810_RCLK_PINS_NUM + \
+					  (_pin) + ZL_REF1P)
+enum ice_cgu_state {
+	ICE_CGU_STATE_UNKNOWN = -1,
+	ICE_CGU_STATE_INVALID,		/* state is not valid */
+	ICE_CGU_STATE_FREERUN,		/* clock is free-running */
+	ICE_CGU_STATE_LOCKED,		/* clock is locked to the reference,
+					 * but the holdover memory is not valid
+					 */
+	ICE_CGU_STATE_LOCKED_HO_ACQ,	/* clock is locked to the reference
+					 * and holdover memory is valid
+					 */
+	ICE_CGU_STATE_HOLDOVER,		/* clock is in holdover mode */
+	ICE_CGU_STATE_MAX
+};
+
+#define MAX_CGU_STATE_NAME_LEN		14
+struct ice_cgu_state_desc {
+	char name[MAX_CGU_STATE_NAME_LEN];
+	enum ice_cgu_state state;
+};
+
+enum ice_zl_cgu_in_pins {
+	ZL_REF0P = 0,
+	ZL_REF0N,
+	ZL_REF1P,
+	ZL_REF1N,
+	ZL_REF2P,
+	ZL_REF2N,
+	ZL_REF3P,
+	ZL_REF3N,
+	ZL_REF4P,
+	ZL_REF4N,
+	NUM_ZL_CGU_INPUT_PINS
+};
+
+enum ice_zl_cgu_out_pins {
+	ZL_OUT0 = 0,
+	ZL_OUT1,
+	ZL_OUT2,
+	ZL_OUT3,
+	ZL_OUT4,
+	ZL_OUT5,
+	ZL_OUT6,
+	NUM_ZL_CGU_OUTPUT_PINS
+};
+
+enum ice_si_cgu_in_pins {
+	SI_REF0P = 0,
+	SI_REF0N,
+	SI_REF1P,
+	SI_REF1N,
+	SI_REF2P,
+	SI_REF2N,
+	SI_REF3,
+	SI_REF4,
+	NUM_SI_CGU_INPUT_PINS
+};
+
+enum ice_si_cgu_out_pins {
+	SI_OUT0 = 0,
+	SI_OUT1,
+	SI_OUT2,
+	SI_OUT3,
+	SI_OUT4,
+	NUM_SI_CGU_OUTPUT_PINS
+};
+
+#define MAX_CGU_PIN_NAME_LEN		16
+#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) | \
+					 BIT(DPLL_PIN_SIGNAL_TYPE_10_MHZ))
+struct ice_cgu_pin_desc {
+	char name[MAX_CGU_PIN_NAME_LEN];
+	u8 index;
+	enum dpll_pin_type type;
+	unsigned long sig_type_mask;
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+			BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
+	{ "NONE",	  SI_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
+	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
+	{ "NONE",	  ZL_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "NONE",	  ZL_REF2P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+			BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
+	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_10_MHZ) },
+	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
+};
+
 extern const struct
 ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
 
@@ -197,6 +424,19 @@ int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data);
 int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data);
 int ice_read_pca9575_reg_e810t(struct ice_hw *hw, u8 offset, u8 *data);
 bool ice_is_pca9575_present(struct ice_hw *hw);
+bool ice_is_phy_rclk_present(struct ice_hw *hw);
+bool ice_is_clock_mux_present_e810t(struct ice_hw *hw);
+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx);
+bool ice_is_cgu_present(struct ice_hw *hw);
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input);
+unsigned long
+ice_cgu_get_pin_sig_type_mask(struct ice_hw *hw, u8 pin, bool input);
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input);
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+		      enum ice_cgu_state last_dpll_state, u8 *pin,
+		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+		      enum ice_cgu_state *dpll_state);
+int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num);
 
 #define PFTSYN_SEM_BYTES	4
 
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index e3f622cad425..c49f573d724f 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -962,6 +962,7 @@ struct ice_hw {
 	DECLARE_BITMAP(hw_ptype, ICE_FLOW_PTYPE_MAX);
 	u8 dvm_ena;
 	u16 io_expander_handle;
+	u8 cgu_part_number;
 };
 
 /* Statistics collected by each port, VSI, VEB, and S-channel */
-- 
2.30.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v5 3/4] ice: add admin commands to access cgu configuration
@ 2023-01-17 18:00   ` Vadim Fedorenko
  0 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, linux-arm-kernel, linux-clk, Vadim Fedorenko

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

Add firmware admin command to access clock generation unit
configuration, it is required to enable Extended PTP and SyncE features
in the driver.
Add definitions of possible hardware variations of input and output pins
related to clock generation unit and functions to access the data.

Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
---
 drivers/net/ethernet/intel/ice/ice.h          |   1 +
 .../net/ethernet/intel/ice/ice_adminq_cmd.h   | 240 ++++++++-
 drivers/net/ethernet/intel/ice/ice_common.c   | 467 ++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_common.h   |  43 ++
 drivers/net/ethernet/intel/ice/ice_lib.c      |  17 +-
 drivers/net/ethernet/intel/ice/ice_ptp_hw.c   | 408 +++++++++++++++
 drivers/net/ethernet/intel/ice/ice_ptp_hw.h   | 240 +++++++++
 drivers/net/ethernet/intel/ice/ice_type.h     |   1 +
 8 files changed, 1412 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 2f0b604abc5e..3368dba42789 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -199,6 +199,7 @@ enum ice_feature {
 	ICE_F_DSCP,
 	ICE_F_PTP_EXTTS,
 	ICE_F_SMA_CTRL,
+	ICE_F_CGU,
 	ICE_F_GNSS,
 	ICE_F_MAX
 };
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 958c1e435232..542b48a2ca92 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -1339,6 +1339,32 @@ struct ice_aqc_set_mac_lb {
 	u8 reserved[15];
 };
 
+/* Set PHY recovered clock output (direct 0x0630) */
+struct ice_aqc_set_phy_rec_clk_out {
+	u8 phy_output;
+	u8 port_num;
+#define ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT	0xFF
+	u8 flags;
+#define ICE_AQC_SET_PHY_REC_CLK_OUT_OUT_EN	BIT(0)
+	u8 rsvd;
+	__le32 freq;
+	u8 rsvd2[6];
+	__le16 node_handle;
+};
+
+/* Get PHY recovered clock output (direct 0x0631) */
+struct ice_aqc_get_phy_rec_clk_out {
+	u8 phy_output;
+	u8 port_num;
+#define ICE_AQC_GET_PHY_REC_CLK_OUT_CURR_PORT	0xFF
+	u8 flags;
+#define ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN	BIT(0)
+	u8 rsvd;
+	__le32 freq;
+	u8 rsvd2[6];
+	__le16 node_handle;
+};
+
 struct ice_aqc_link_topo_params {
 	u8 lport_num;
 	u8 lport_num_valid;
@@ -1355,6 +1381,8 @@ struct ice_aqc_link_topo_params {
 #define ICE_AQC_LINK_TOPO_NODE_TYPE_CAGE	6
 #define ICE_AQC_LINK_TOPO_NODE_TYPE_MEZZ	7
 #define ICE_AQC_LINK_TOPO_NODE_TYPE_ID_EEPROM	8
+#define ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL	9
+#define ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_MUX	10
 #define ICE_AQC_LINK_TOPO_NODE_CTX_S		4
 #define ICE_AQC_LINK_TOPO_NODE_CTX_M		\
 				(0xF << ICE_AQC_LINK_TOPO_NODE_CTX_S)
@@ -1391,7 +1419,12 @@ struct ice_aqc_link_topo_addr {
 struct ice_aqc_get_link_topo {
 	struct ice_aqc_link_topo_addr addr;
 	u8 node_part_num;
-#define ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575	0x21
+#define ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575		0x21
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032	0x24
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384	0x25
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_E822_PHY		0x30
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827		0x31
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_GEN_CLK_MUX	0x47
 	u8 rsvd[9];
 };
 
@@ -2066,6 +2099,186 @@ struct ice_aqc_get_pkg_info_resp {
 	struct ice_aqc_get_pkg_info pkg_info[];
 };
 
+/* Get CGU abilities command response data structure (indirect 0x0C61) */
+struct ice_aqc_get_cgu_abilities {
+	u8 num_inputs;
+	u8 num_outputs;
+	u8 pps_dpll_idx;
+	u8 eec_dpll_idx;
+	__le32 max_in_freq;
+	__le32 max_in_phase_adj;
+	__le32 max_out_freq;
+	__le32 max_out_phase_adj;
+	u8 cgu_part_num;
+	u8 rsvd[3];
+};
+
+/* Set CGU input config (direct 0x0C62) */
+struct ice_aqc_set_cgu_input_config {
+	u8 input_idx;
+	u8 flags1;
+#define ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ		BIT(6)
+#define ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_DELAY	BIT(7)
+	u8 flags2;
+#define ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN		BIT(5)
+#define ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN		BIT(6)
+	u8 rsvd;
+	__le32 freq;
+	__le32 phase_delay;
+	u8 rsvd2[2];
+	__le16 node_handle;
+};
+
+/* Get CGU input config response descriptor structure (direct 0x0C63) */
+struct ice_aqc_get_cgu_input_config {
+	u8 input_idx;
+	u8 status;
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_LOS		BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_SCM_FAIL		BIT(1)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_CFM_FAIL		BIT(2)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_GST_FAIL		BIT(3)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_PFM_FAIL		BIT(4)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_ESYNC_FAIL	BIT(6)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_ESYNC_CAP		BIT(7)
+	u8 type;
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_READ_ONLY		BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_GPS			BIT(4)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_EXTERNAL		BIT(5)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_PHY			BIT(6)
+	u8 flags1;
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_PHASE_DELAY_SUPP	BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_1PPS_SUPP		BIT(2)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_10MHZ_SUPP		BIT(3)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_ANYFREQ		BIT(7)
+	__le32 freq;
+	__le32 phase_delay;
+	u8 flags2;
+#define ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN		BIT(5)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN		BIT(6)
+	u8 rsvd[1];
+	__le16 node_handle;
+};
+
+/* Set CGU output config (direct 0x0C64) */
+struct ice_aqc_set_cgu_output_config {
+	u8 output_idx;
+	u8 flags;
+#define ICE_AQC_SET_CGU_OUT_CFG_OUT_EN		BIT(0)
+#define ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN	BIT(1)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ     BIT(2)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_PHASE    BIT(3)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_SRC_SEL  BIT(4)
+	u8 src_sel;
+#define ICE_AQC_SET_CGU_OUT_CFG_DPLL_SRC_SEL    ICE_M(0x1F, 0)
+	u8 rsvd;
+	__le32 freq;
+	__le32 phase_delay;
+	u8 rsvd2[2];
+	__le16 node_handle;
+};
+
+/* Get CGU output config (direct 0x0C65) */
+struct ice_aqc_get_cgu_output_config {
+	u8 output_idx;
+	u8 flags;
+#define ICE_AQC_GET_CGU_OUT_CFG_OUT_EN		BIT(0)
+#define ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN	BIT(1)
+#define ICE_AQC_GET_CGU_OUT_CFG_ESYNC_ABILITY	BIT(2)
+	u8 src_sel;
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL_SHIFT	0
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL \
+	ICE_M(0x1F, ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL_SHIFT)
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE_SHIFT		5
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE \
+	ICE_M(0x7, ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE_SHIFT)
+	u8 rsvd;
+	__le32 freq;
+	__le32 src_freq;
+	u8 rsvd2[2];
+	__le16 node_handle;
+};
+
+/* Get CGU DPLL status (direct 0x0C66) */
+struct ice_aqc_get_cgu_dpll_status {
+	u8 dpll_num;
+	u8 ref_state;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_LOS		BIT(0)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_SCM		BIT(1)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_CFM		BIT(2)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_GST		BIT(3)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_PFM		BIT(4)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_FAST_LOCK_EN	BIT(5)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_ESYNC	BIT(6)
+	__le16 dpll_state;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_LOCK		BIT(0)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO		BIT(1)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO_READY	BIT(2)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_FLHIT		BIT(5)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_PSLHIT	BIT(7)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT	8
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SEL	\
+	ICE_M(0x1F, ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE_SHIFT	13
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE \
+	ICE_M(0x7, ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE_SHIFT)
+	__le32 phase_offset_h;
+	__le32 phase_offset_l;
+	u8 eec_mode;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_1		0xA
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_2		0xB
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_UNKNOWN	0xF
+	u8 rsvd[1];
+	__le16 node_handle;
+};
+
+/* Set CGU DPLL config (direct 0x0C67) */
+struct ice_aqc_set_cgu_dpll_config {
+	u8 dpll_num;
+	u8 ref_state;
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_LOS		BIT(0)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_SCM		BIT(1)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_CFM		BIT(2)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_GST		BIT(3)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_PFM		BIT(4)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_FLOCK_EN	BIT(5)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_ESYNC	BIT(6)
+	u8 rsvd;
+	u8 config;
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_CLK_REF_SEL		ICE_M(0x1F, 0)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE		ICE_M(0x7, 5)
+	u8 rsvd2[8];
+	u8 eec_mode;
+	u8 rsvd3[1];
+	__le16 node_handle;
+};
+
+/* Set CGU reference priority (direct 0x0C68) */
+struct ice_aqc_set_cgu_ref_prio {
+	u8 dpll_num;
+	u8 ref_idx;
+	u8 ref_priority;
+	u8 rsvd[11];
+	__le16 node_handle;
+};
+
+/* Get CGU reference priority (direct 0x0C69) */
+struct ice_aqc_get_cgu_ref_prio {
+	u8 dpll_num;
+	u8 ref_idx;
+	u8 ref_priority; /* Valid only in response */
+	u8 rsvd[13];
+};
+
+/* Get CGU info (direct 0x0C6A) */
+struct ice_aqc_get_cgu_info {
+	__le32 cgu_id;
+	__le32 cgu_cfg_ver;
+	__le32 cgu_fw_ver;
+	u8 node_part_num;
+	u8 dev_rev;
+	__le16 node_handle;
+};
+
 /* Driver Shared Parameters (direct, 0x0C90) */
 struct ice_aqc_driver_shared_params {
 	u8 set_or_get_op;
@@ -2135,6 +2348,8 @@ struct ice_aq_desc {
 		struct ice_aqc_get_phy_caps get_phy;
 		struct ice_aqc_set_phy_cfg set_phy;
 		struct ice_aqc_restart_an restart_an;
+		struct ice_aqc_set_phy_rec_clk_out set_phy_rec_clk_out;
+		struct ice_aqc_get_phy_rec_clk_out get_phy_rec_clk_out;
 		struct ice_aqc_gpio read_write_gpio;
 		struct ice_aqc_sff_eeprom read_write_sff_param;
 		struct ice_aqc_set_port_id_led set_port_id_led;
@@ -2174,6 +2389,15 @@ struct ice_aq_desc {
 		struct ice_aqc_fw_logging fw_logging;
 		struct ice_aqc_get_clear_fw_log get_clear_fw_log;
 		struct ice_aqc_download_pkg download_pkg;
+		struct ice_aqc_set_cgu_input_config set_cgu_input_config;
+		struct ice_aqc_get_cgu_input_config get_cgu_input_config;
+		struct ice_aqc_set_cgu_output_config set_cgu_output_config;
+		struct ice_aqc_get_cgu_output_config get_cgu_output_config;
+		struct ice_aqc_get_cgu_dpll_status get_cgu_dpll_status;
+		struct ice_aqc_set_cgu_dpll_config set_cgu_dpll_config;
+		struct ice_aqc_set_cgu_ref_prio set_cgu_ref_prio;
+		struct ice_aqc_get_cgu_ref_prio get_cgu_ref_prio;
+		struct ice_aqc_get_cgu_info get_cgu_info;
 		struct ice_aqc_driver_shared_params drv_shared_params;
 		struct ice_aqc_set_mac_lb set_mac_lb;
 		struct ice_aqc_alloc_free_res_cmd sw_res_ctrl;
@@ -2297,6 +2521,8 @@ enum ice_adminq_opc {
 	ice_aqc_opc_get_link_status			= 0x0607,
 	ice_aqc_opc_set_event_mask			= 0x0613,
 	ice_aqc_opc_set_mac_lb				= 0x0620,
+	ice_aqc_opc_set_phy_rec_clk_out			= 0x0630,
+	ice_aqc_opc_get_phy_rec_clk_out			= 0x0631,
 	ice_aqc_opc_get_link_topo			= 0x06E0,
 	ice_aqc_opc_read_i2c				= 0x06E2,
 	ice_aqc_opc_write_i2c				= 0x06E3,
@@ -2350,6 +2576,18 @@ enum ice_adminq_opc {
 	ice_aqc_opc_update_pkg				= 0x0C42,
 	ice_aqc_opc_get_pkg_info_list			= 0x0C43,
 
+	/* 1588/SyncE commands/events */
+	ice_aqc_opc_get_cgu_abilities			= 0x0C61,
+	ice_aqc_opc_set_cgu_input_config		= 0x0C62,
+	ice_aqc_opc_get_cgu_input_config		= 0x0C63,
+	ice_aqc_opc_set_cgu_output_config		= 0x0C64,
+	ice_aqc_opc_get_cgu_output_config		= 0x0C65,
+	ice_aqc_opc_get_cgu_dpll_status			= 0x0C66,
+	ice_aqc_opc_set_cgu_dpll_config			= 0x0C67,
+	ice_aqc_opc_set_cgu_ref_prio			= 0x0C68,
+	ice_aqc_opc_get_cgu_ref_prio			= 0x0C69,
+	ice_aqc_opc_get_cgu_info			= 0x0C6A,
+
 	ice_aqc_opc_driver_shared_params		= 0x0C90,
 
 	/* Standalone Commands/Events */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index d02b55b6aa9c..67c8934ee8f9 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -409,6 +409,83 @@ ice_aq_get_link_topo_handle(struct ice_port_info *pi, u8 node_type,
 	return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd);
 }
 
+/**
+ * ice_aq_get_netlist_node
+ * @hw: pointer to the hw struct
+ * @cmd: get_link_topo AQ structure
+ * @node_part_number: output node part number if node found
+ * @node_handle: output node handle parameter if node found
+ *
+ * Get netlist node handle.
+ */
+int
+ice_aq_get_netlist_node(struct ice_hw *hw, struct ice_aqc_get_link_topo *cmd,
+			u8 *node_part_number, u16 *node_handle)
+{
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_link_topo);
+	desc.params.get_link_topo = *cmd;
+
+	if (ice_aq_send_cmd(hw, &desc, NULL, 0, NULL))
+		return -EINTR;
+
+	if (node_handle)
+		*node_handle =
+			le16_to_cpu(desc.params.get_link_topo.addr.handle);
+	if (node_part_number)
+		*node_part_number = desc.params.get_link_topo.node_part_num;
+
+	return 0;
+}
+
+#define MAX_NETLIST_SIZE	10
+
+/**
+ * ice_find_netlist_node
+ * @hw: pointer to the hw struct
+ * @node_type_ctx: type of netlist node to look for
+ * @node_part_number: node part number to look for
+ * @node_handle: output parameter if node found - optional
+ *
+ * Find and return the node handle for a given node type and part number in the
+ * netlist. When found ICE_SUCCESS is returned, ICE_ERR_DOES_NOT_EXIST
+ * otherwise. If node_handle provided, it would be set to found node handle.
+ */
+int
+ice_find_netlist_node(struct ice_hw *hw, u8 node_type_ctx, u8 node_part_number,
+		      u16 *node_handle)
+{
+	struct ice_aqc_get_link_topo cmd;
+	u8 rec_node_part_number;
+	u16 rec_node_handle;
+	u8 idx;
+
+	for (idx = 0; idx < MAX_NETLIST_SIZE; idx++) {
+		int status;
+
+		memset(&cmd, 0, sizeof(cmd));
+
+		cmd.addr.topo_params.node_type_ctx =
+			(node_type_ctx << ICE_AQC_LINK_TOPO_NODE_TYPE_S);
+		cmd.addr.topo_params.index = idx;
+
+		status = ice_aq_get_netlist_node(hw, &cmd,
+						 &rec_node_part_number,
+						 &rec_node_handle);
+		if (status)
+			return status;
+
+		if (rec_node_part_number == node_part_number) {
+			if (node_handle)
+				*node_handle = rec_node_handle;
+			return 0;
+		}
+	}
+
+	return -ENOTBLK;
+}
+
 /**
  * ice_is_media_cage_present
  * @pi: port information structure
@@ -4904,6 +4981,396 @@ ice_dis_vsi_rdma_qset(struct ice_port_info *pi, u16 count, u32 *qset_teid,
 	return status;
 }
 
+/**
+ * ice_aq_get_cgu_abilities
+ * @hw: pointer to the HW struct
+ * @abilities: CGU abilities
+ *
+ * Get CGU abilities (0x0C61)
+ */
+int
+ice_aq_get_cgu_abilities(struct ice_hw *hw,
+			 struct ice_aqc_get_cgu_abilities *abilities)
+{
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_abilities);
+	return ice_aq_send_cmd(hw, &desc, abilities, sizeof(*abilities), NULL);
+}
+
+/**
+ * ice_aq_set_input_pin_cfg
+ * @hw: pointer to the HW struct
+ * @input_idx: Input index
+ * @flags1: Input flags
+ * @flags2: Input flags
+ * @freq: Frequency in Hz
+ * @phase_delay: Delay in ps
+ *
+ * Set CGU input config (0x0C62)
+ */
+int
+ice_aq_set_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 flags1, u8 flags2,
+			 u32 freq, s32 phase_delay)
+{
+	struct ice_aqc_set_cgu_input_config *cmd;
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_input_config);
+	cmd = &desc.params.set_cgu_input_config;
+	cmd->input_idx = input_idx;
+	cmd->flags1 = flags1;
+	cmd->flags2 = flags2;
+	cmd->freq = cpu_to_le32(freq);
+	cmd->phase_delay = cpu_to_le32(phase_delay);
+
+	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_input_pin_cfg
+ * @hw: pointer to the HW struct
+ * @input_idx: Input index
+ * @status: Pin status
+ * @type: Pin type
+ * @flags1: Input flags
+ * @flags2: Input flags
+ * @freq: Frequency in Hz
+ * @phase_delay: Delay in ps
+ *
+ * Get CGU input config (0x0C63)
+ */
+int
+ice_aq_get_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 *status, u8 *type,
+			 u8 *flags1, u8 *flags2, u32 *freq, s32 *phase_delay)
+{
+	struct ice_aqc_get_cgu_input_config *cmd;
+	struct ice_aq_desc desc;
+	int ret;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_input_config);
+	cmd = &desc.params.get_cgu_input_config;
+	cmd->input_idx = input_idx;
+
+	ret = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!ret) {
+		if (status)
+			*status = cmd->status;
+		if (type)
+			*type = cmd->type;
+		if (flags1)
+			*flags1 = cmd->flags1;
+		if (flags2)
+			*flags2 = cmd->flags2;
+		if (freq)
+			*freq = le32_to_cpu(cmd->freq);
+		if (phase_delay)
+			*phase_delay = le32_to_cpu(cmd->phase_delay);
+	}
+
+	return ret;
+}
+
+/**
+ * ice_aq_set_output_pin_cfg
+ * @hw: pointer to the HW struct
+ * @output_idx: Output index
+ * @flags: Output flags
+ * @src_sel: Index of DPLL block
+ * @freq: Output frequency
+ * @phase_delay: Output phase compensation
+ *
+ * Set CGU output config (0x0C64)
+ */
+int
+ice_aq_set_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 flags,
+			  u8 src_sel, u32 freq, s32 phase_delay)
+{
+	struct ice_aqc_set_cgu_output_config *cmd;
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_output_config);
+	cmd = &desc.params.set_cgu_output_config;
+	cmd->output_idx = output_idx;
+	cmd->flags = flags;
+	cmd->src_sel = src_sel;
+	cmd->freq = cpu_to_le32(freq);
+	cmd->phase_delay = cpu_to_le32(phase_delay);
+
+	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_output_pin_cfg
+ * @hw: pointer to the HW struct
+ * @output_idx: Output index
+ * @flags: Output flags
+ * @src_sel: Internal DPLL source
+ * @freq: Output frequency
+ * @src_freq: Source frequency
+ *
+ * Get CGU output config (0x0C65)
+ */
+int
+ice_aq_get_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 *flags,
+			  u8 *src_sel, u32 *freq, u32 *src_freq)
+{
+	struct ice_aqc_get_cgu_output_config *cmd;
+	struct ice_aq_desc desc;
+	int ret;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_output_config);
+	cmd = &desc.params.get_cgu_output_config;
+	cmd->output_idx = output_idx;
+
+	ret = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!ret) {
+		if (flags)
+			*flags = cmd->flags;
+		if (src_sel)
+			*src_sel = cmd->src_sel;
+		if (freq)
+			*freq = le32_to_cpu(cmd->freq);
+		if (src_freq)
+			*src_freq = le32_to_cpu(cmd->src_freq);
+	}
+
+	return ret;
+}
+
+/**
+ * convert_s48_to_s64 - convert 48 bit value to 64 bit value
+ * @signed_48: signed 64 bit variable storing signed 48 bit value
+ *
+ * Convert signed 48 bit value to its 64 bit representation.
+ *
+ * Return: signed 64 bit representation of signed 48 bit value.
+ */
+static inline
+s64 convert_s48_to_s64(s64 signed_48)
+{
+	const s64 MASK_SIGN_BITS = GENMASK_ULL(63, 48);
+	const s64 SIGN_BIT_47 = BIT_ULL(47);
+
+	return ((signed_48 & SIGN_BIT_47) ? (s64)(MASK_SIGN_BITS | signed_48)
+		: signed_48);
+}
+
+/**
+ * ice_aq_get_cgu_dpll_status
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_state: Reference clock state
+ * @dpll_state: DPLL state
+ * @phase_offset: Phase offset in ns
+ * @eec_mode: EEC_mode
+ *
+ * Get CGU DPLL status (0x0C66)
+ */
+int
+ice_aq_get_cgu_dpll_status(struct ice_hw *hw, u8 dpll_num, u8 *ref_state,
+			   u16 *dpll_state, s64 *phase_offset, u8 *eec_mode)
+{
+	struct ice_aqc_get_cgu_dpll_status *cmd;
+	const s64 NSEC_PER_PSEC = 1000LL;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_dpll_status);
+	cmd = &desc.params.get_cgu_dpll_status;
+	cmd->dpll_num = dpll_num;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status) {
+		*ref_state = cmd->ref_state;
+		*dpll_state = le16_to_cpu(cmd->dpll_state);
+		*phase_offset = le32_to_cpu(cmd->phase_offset_h);
+		*phase_offset <<= 32;
+		*phase_offset += le32_to_cpu(cmd->phase_offset_l);
+		*phase_offset = convert_s48_to_s64(*phase_offset)
+				/ NSEC_PER_PSEC;
+		*eec_mode = cmd->eec_mode;
+	}
+
+	return status;
+}
+
+/**
+ * ice_aq_set_cgu_dpll_config
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_state: Reference clock state
+ * @config: DPLL config
+ * @eec_mode: EEC mode
+ *
+ * Set CGU DPLL config (0x0C67)
+ */
+int
+ice_aq_set_cgu_dpll_config(struct ice_hw *hw, u8 dpll_num, u8 ref_state,
+			   u8 config, u8 eec_mode)
+{
+	struct ice_aqc_set_cgu_dpll_config *cmd;
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_dpll_config);
+	cmd = &desc.params.set_cgu_dpll_config;
+	cmd->dpll_num = dpll_num;
+	cmd->ref_state = ref_state;
+	cmd->config = config;
+	cmd->eec_mode = eec_mode;
+
+	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_set_cgu_ref_prio
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_idx: Reference pin index
+ * @ref_priority: Reference input priority
+ *
+ * Set CGU reference priority (0x0C68)
+ */
+int
+ice_aq_set_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+			u8 ref_priority)
+{
+	struct ice_aqc_set_cgu_ref_prio *cmd;
+	struct ice_aq_desc desc;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_ref_prio);
+	cmd = &desc.params.set_cgu_ref_prio;
+	cmd->dpll_num = dpll_num;
+	cmd->ref_idx = ref_idx;
+	cmd->ref_priority = ref_priority;
+
+	return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_cgu_ref_prio
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_idx: Reference pin index
+ * @ref_prio: Reference input priority
+ *
+ * Get CGU reference priority (0x0C69)
+ */
+int
+ice_aq_get_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+			u8 *ref_prio)
+{
+	struct ice_aqc_get_cgu_ref_prio *cmd;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_ref_prio);
+	cmd = &desc.params.get_cgu_ref_prio;
+	cmd->dpll_num = dpll_num;
+	cmd->ref_idx = ref_idx;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status)
+		*ref_prio = cmd->ref_priority;
+
+	return status;
+}
+
+/**
+ * ice_aq_get_cgu_info
+ * @hw: pointer to the HW struct
+ * @cgu_id: CGU ID
+ * @cgu_cfg_ver: CGU config version
+ * @cgu_fw_ver: CGU firmware version
+ *
+ * Get CGU info (0x0C6A)
+ */
+int
+ice_aq_get_cgu_info(struct ice_hw *hw, u32 *cgu_id, u32 *cgu_cfg_ver,
+		    u32 *cgu_fw_ver)
+{
+	struct ice_aqc_get_cgu_info *cmd;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_info);
+	cmd = &desc.params.get_cgu_info;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status) {
+		*cgu_id = le32_to_cpu(cmd->cgu_id);
+		*cgu_cfg_ver = le32_to_cpu(cmd->cgu_cfg_ver);
+		*cgu_fw_ver = le32_to_cpu(cmd->cgu_fw_ver);
+	}
+
+	return status;
+}
+
+/**
+ * ice_aq_set_phy_rec_clk_out - set RCLK phy out
+ * @hw: pointer to the HW struct
+ * @phy_output: PHY reference clock output pin
+ * @enable: GPIO state to be applied
+ * @freq: PHY output frequency
+ *
+ * Set CGU reference priority (0x0630)
+ * Return 0 on success or negative value on failure.
+ */
+int
+ice_aq_set_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, bool enable,
+			   u32 *freq)
+{
+	struct ice_aqc_set_phy_rec_clk_out *cmd;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_phy_rec_clk_out);
+	cmd = &desc.params.set_phy_rec_clk_out;
+	cmd->phy_output = phy_output;
+	cmd->port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
+	cmd->flags = enable & ICE_AQC_SET_PHY_REC_CLK_OUT_OUT_EN;
+	cmd->freq = cpu_to_le32(*freq);
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status)
+		*freq = le32_to_cpu(cmd->freq);
+
+	return status;
+}
+
+/**
+ * ice_aq_get_phy_rec_clk_out
+ * @hw: pointer to the HW struct
+ * @phy_output: PHY reference clock output pin
+ * @port_num: Port number
+ * @flags: PHY flags
+ * @freq: PHY output frequency
+ *
+ * Get PHY recovered clock output (0x0631)
+ */
+int
+ice_aq_get_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, u8 *port_num,
+			   u8 *flags, u32 *freq)
+{
+	struct ice_aqc_get_phy_rec_clk_out *cmd;
+	struct ice_aq_desc desc;
+	int status;
+
+	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_phy_rec_clk_out);
+	cmd = &desc.params.get_phy_rec_clk_out;
+	cmd->phy_output = phy_output;
+	cmd->port_num = *port_num;
+
+	status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+	if (!status) {
+		*port_num = cmd->port_num;
+		*flags = cmd->flags;
+		*freq = le32_to_cpu(cmd->freq);
+	}
+
+	return status;
+}
+
 /**
  * ice_replay_pre_init - replay pre initialization
  * @hw: pointer to the HW struct
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
index 4c6a0b5c9304..637c3e5eea8d 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.h
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -94,6 +94,12 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
 		    struct ice_aqc_get_phy_caps_data *caps,
 		    struct ice_sq_cd *cd);
 int
+ice_find_netlist_node(struct ice_hw *hw, u8 node_type_ctx, u8 node_part_number,
+		      u16 *node_handle);
+int
+ice_aq_get_netlist_node(struct ice_hw *hw, struct ice_aqc_get_link_topo *cmd,
+			u8 *node_part_number, u16 *node_handle);
+int
 ice_aq_list_caps(struct ice_hw *hw, void *buf, u16 buf_size, u32 *cap_count,
 		 enum ice_adminq_opc opc, struct ice_sq_cd *cd);
 int
@@ -192,6 +198,43 @@ void ice_output_fw_log(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf);
 struct ice_q_ctx *
 ice_get_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 q_handle);
 int ice_sbq_rw_reg(struct ice_hw *hw, struct ice_sbq_msg_input *in);
+int
+ice_aq_get_cgu_abilities(struct ice_hw *hw,
+			 struct ice_aqc_get_cgu_abilities *abilities);
+int
+ice_aq_set_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 flags1, u8 flags2,
+			 u32 freq, s32 phase_delay);
+int
+ice_aq_get_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 *status, u8 *type,
+			 u8 *flags1, u8 *flags2, u32 *freq, s32 *phase_delay);
+int
+ice_aq_set_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 flags,
+			  u8 src_sel, u32 freq, s32 phase_delay);
+int
+ice_aq_get_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 *flags,
+			  u8 *src_sel, u32 *freq, u32 *src_freq);
+int
+ice_aq_get_cgu_dpll_status(struct ice_hw *hw, u8 dpll_num, u8 *ref_state,
+			   u16 *dpll_state, s64 *phase_offset, u8 *eec_mode);
+int
+ice_aq_set_cgu_dpll_config(struct ice_hw *hw, u8 dpll_num, u8 ref_state,
+			   u8 config, u8 eec_mode);
+int
+ice_aq_set_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+			u8 ref_priority);
+int
+ice_aq_get_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+			u8 *ref_prio);
+int
+ice_aq_get_cgu_info(struct ice_hw *hw, u32 *cgu_id, u32 *cgu_cfg_ver,
+		    u32 *cgu_fw_ver);
+
+int
+ice_aq_set_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, bool enable,
+			   u32 *freq);
+int
+ice_aq_get_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, u8 *port_num,
+			   u8 *flags, u32 *freq);
 void
 ice_stat_update40(struct ice_hw *hw, u32 reg, bool prev_stat_loaded,
 		  u64 *prev_stat, u64 *cur_stat);
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 94aa834cd9a6..6d965e030772 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4426,13 +4426,22 @@ void ice_init_feature_support(struct ice_pf *pf)
 	case ICE_DEV_ID_E810C_BACKPLANE:
 	case ICE_DEV_ID_E810C_QSFP:
 	case ICE_DEV_ID_E810C_SFP:
+	case ICE_DEV_ID_E810_XXV_BACKPLANE:
+	case ICE_DEV_ID_E810_XXV_QSFP:
+	case ICE_DEV_ID_E810_XXV_SFP:
 		ice_set_feature_support(pf, ICE_F_DSCP);
 		ice_set_feature_support(pf, ICE_F_PTP_EXTTS);
-		if (ice_is_e810t(&pf->hw)) {
+		if (ice_is_phy_rclk_present(&pf->hw))
+			ice_set_feature_support(pf, ICE_F_PHY_RCLK);
+		/* If we don't own the timer - don't enable other caps */
+		if (!pf->hw.func_caps.ts_func_info.src_tmr_owned)
+			break;
+		if (ice_is_cgu_present(&pf->hw))
+			ice_set_feature_support(pf, ICE_F_CGU);
+		if (ice_is_clock_mux_present_e810t(&pf->hw))
 			ice_set_feature_support(pf, ICE_F_SMA_CTRL);
-			if (ice_gnss_is_gps_present(&pf->hw))
-				ice_set_feature_support(pf, ICE_F_GNSS);
-		}
+		if (ice_gnss_is_gps_present(&pf->hw))
+			ice_set_feature_support(pf, ICE_F_GNSS);
 		break;
 	default:
 		break;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index a38614d21ea8..01bf15941f85 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -3213,6 +3213,91 @@ ice_get_pca9575_handle(struct ice_hw *hw, u16 *pca9575_handle)
 	return 0;
 }
 
+/**
+ * ice_is_phy_rclk_present
+ * @hw: pointer to the hw struct
+ *
+ * Check if the PHY Recovered Clock device is present in the netlist
+ * Return:
+ * * true - device found in netlist
+ * * false - device not found
+ */
+bool ice_is_phy_rclk_present(struct ice_hw *hw)
+{
+	if (ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+				  ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827, NULL) &&
+	    ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+				  ICE_ACQ_GET_LINK_TOPO_NODE_NR_E822_PHY, NULL))
+		return false;
+
+	return true;
+}
+
+/**
+ * ice_is_clock_mux_present_e810t
+ * @hw: pointer to the hw struct
+ *
+ * Check if the Clock Multiplexer device is present in the netlist
+ * Return:
+ * * true - device found in netlist
+ * * false - device not found
+ */
+bool ice_is_clock_mux_present_e810t(struct ice_hw *hw)
+{
+	if (ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_MUX,
+				  ICE_ACQ_GET_LINK_TOPO_NODE_NR_GEN_CLK_MUX,
+				  NULL))
+		return false;
+
+	return true;
+}
+
+/**
+ * ice_get_pf_c827_idx - find and return the C827 index for the current pf
+ * @hw: pointer to the hw struct
+ * @idx: index of the found C827 PHY
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx)
+{
+	struct ice_aqc_get_link_topo cmd;
+	u8 node_part_number;
+	u16 node_handle;
+	int status;
+	u8 ctx;
+
+	if (hw->mac_type != ICE_MAC_E810)
+		return -ENODEV;
+
+	if (hw->device_id != ICE_DEV_ID_E810C_QSFP) {
+		*idx = C827_0;
+		return 0;
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	ctx = ICE_AQC_LINK_TOPO_NODE_TYPE_PHY << ICE_AQC_LINK_TOPO_NODE_TYPE_S;
+	ctx |= ICE_AQC_LINK_TOPO_NODE_CTX_PORT << ICE_AQC_LINK_TOPO_NODE_CTX_S;
+	cmd.addr.topo_params.node_type_ctx = ctx;
+	cmd.addr.topo_params.index = 0;
+
+	status = ice_aq_get_netlist_node(hw, &cmd, &node_part_number,
+					 &node_handle);
+	if (status || node_part_number != ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827)
+		return -ENOENT;
+
+	if (node_handle == E810C_QSFP_C827_0_HANDLE)
+		*idx = C827_0;
+	else if (node_handle == E810C_QSFP_C827_1_HANDLE)
+		*idx = C827_1;
+	else
+		return -EIO;
+
+	return 0;
+}
+
 /**
  * ice_read_sma_ctrl_e810t
  * @hw: pointer to the hw struct
@@ -3381,3 +3466,326 @@ int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready)
 		return ice_get_phy_tx_tstamp_ready_e822(hw, block,
 							tstamp_ready);
 }
+
+/**
+ * ice_is_cgu_present
+ * @hw: pointer to the hw struct
+ *
+ * Check if the Clock Generation Unit (CGU) device is present in the netlist
+ * Return:
+ * * true - cgu is present
+ * * false - cgu is not present
+ */
+bool ice_is_cgu_present(struct ice_hw *hw)
+{
+	if (!ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+				   ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032,
+				   NULL)) {
+		hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032;
+		return true;
+	} else if (!ice_find_netlist_node(hw,
+					  ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+					  ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384,
+					  NULL)) {
+		hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384;
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * ice_cgu_get_pin_desc_e823
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pin
+ * @size: number of inputs/outputs
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc_e823(struct ice_hw *hw, bool input, int *size)
+{
+	static const struct ice_cgu_pin_desc *t;
+
+	if (hw->cgu_part_number ==
+	    ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032) {
+		if (input) {
+			t = ice_e823_zl_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e823_zl_cgu_inputs);
+		} else {
+			t = ice_e823_zl_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e823_zl_cgu_outputs);
+		}
+	} else if (hw->cgu_part_number ==
+		   ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384) {
+		if (input) {
+			t = ice_e823_si_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e823_si_cgu_inputs);
+		} else {
+			t = ice_e823_si_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e823_si_cgu_outputs);
+		}
+	} else {
+		t = NULL;
+		*size = 0;
+	}
+
+	return t;
+}
+
+/**
+ * ice_cgu_get_pin_desc
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pins
+ * @size: size of array returned by function
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc(struct ice_hw *hw, bool input, int *size)
+{
+	const struct ice_cgu_pin_desc *t = NULL;
+
+	switch (hw->device_id) {
+	case ICE_DEV_ID_E810C_SFP:
+		if (input) {
+			t = ice_e810t_sfp_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e810t_sfp_cgu_inputs);
+		} else {
+			t = ice_e810t_sfp_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e810t_sfp_cgu_outputs);
+		}
+		break;
+	case ICE_DEV_ID_E810C_QSFP:
+		if (input) {
+			t = ice_e810t_qsfp_cgu_inputs;
+			*size = ARRAY_SIZE(ice_e810t_qsfp_cgu_inputs);
+		} else {
+			t = ice_e810t_qsfp_cgu_outputs;
+			*size = ARRAY_SIZE(ice_e810t_qsfp_cgu_outputs);
+		}
+		break;
+	case ICE_DEV_ID_E823L_10G_BASE_T:
+	case ICE_DEV_ID_E823L_1GBE:
+	case ICE_DEV_ID_E823L_BACKPLANE:
+	case ICE_DEV_ID_E823L_QSFP:
+	case ICE_DEV_ID_E823L_SFP:
+	case ICE_DEV_ID_E823C_10G_BASE_T:
+	case ICE_DEV_ID_E823C_BACKPLANE:
+	case ICE_DEV_ID_E823C_QSFP:
+	case ICE_DEV_ID_E823C_SFP:
+	case ICE_DEV_ID_E823C_SGMII:
+		t = ice_cgu_get_pin_desc_e823(hw, input, size);
+		break;
+	default:
+		break;
+	}
+
+	return t;
+}
+
+/**
+ * ice_cgu_get_pin_type
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: type of a pin.
+ */
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return 0;
+
+	if (pin >= t_size)
+		return 0;
+
+	return t[pin].type;
+}
+
+/**
+ * ice_cgu_get_pin_sig_type_mask
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: signal type bit mask of a pin.
+ */
+unsigned long
+ice_cgu_get_pin_sig_type_mask(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return 0;
+
+	if (pin >= t_size)
+		return 0;
+
+	return t[pin].sig_type_mask;
+}
+
+/**
+ * ice_cgu_get_pin_name
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return:
+ * * null terminated char array with name
+ * * NULL in case of failure
+ */
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input)
+{
+	const struct ice_cgu_pin_desc *t;
+	int t_size;
+
+	t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+	if (!t)
+		return NULL;
+
+	if (pin >= t_size)
+		return NULL;
+
+	return t[pin].name;
+}
+
+/**
+ * ice_get_cgu_state - get the state of the DPLL
+ * @hw: pointer to the hw struct
+ * @dpll_idx: Index of internal DPLL unit
+ * @last_dpll_state: last known state of DPLL
+ * @pin: pointer to a buffer for returning currently active pin
+ * @ref_state: reference clock state
+ * @phase_offset: pointer to a buffer for returning phase offset
+ * @dpll_state: state of the DPLL (output)
+ *
+ * This function will read the state of the DPLL(dpll_idx). Non-null
+ * 'pin', 'ref_state', 'eec_mode' and 'phase_offset' parameters are used to
+ * retrieve currently active pin, state, mode and phase_offset respectively.
+ *
+ * Return: state of the DPLL
+ */
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+		      enum ice_cgu_state last_dpll_state, u8 *pin,
+		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+		      enum ice_cgu_state *dpll_state)
+{
+	u8 hw_ref_state, hw_eec_mode;
+	s64 hw_phase_offset;
+	u16 hw_dpll_state;
+	int status;
+
+	status = ice_aq_get_cgu_dpll_status(hw, dpll_idx, &hw_ref_state,
+					    &hw_dpll_state, &hw_phase_offset,
+					    &hw_eec_mode);
+	if (status) {
+		*dpll_state = ICE_CGU_STATE_INVALID;
+		return status;
+	}
+
+	if (pin) {
+		/* current ref pin in dpll_state_refsel_status_X register */
+		*pin = (hw_dpll_state &
+			ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SEL) >>
+		       ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT;
+	}
+
+	if (phase_offset)
+		*phase_offset = hw_phase_offset;
+
+	if (ref_state)
+		*ref_state = hw_ref_state;
+
+	if (eec_mode)
+		*eec_mode = hw_eec_mode;
+
+	if (!dpll_state)
+		return status;
+
+	/* According to ZL DPLL documentation, once state reach LOCKED_HO_ACQ
+	 * it would never return to FREERUN. This aligns to ITU-T G.781
+	 * Recommendation. We cannot report HOLDOVER as HO memory is cleared
+	 * while switching to another reference.
+	 * Only for situations where previous state was either: "LOCKED without
+	 * HO_ACQ" or "HOLDOVER" we actually back to FREERUN.
+	 */
+	if (hw_dpll_state & ICE_AQC_GET_CGU_DPLL_STATUS_STATE_LOCK) {
+		if (hw_dpll_state & ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO_READY)
+			*dpll_state = ICE_CGU_STATE_LOCKED_HO_ACQ;
+		else
+			*dpll_state = ICE_CGU_STATE_LOCKED;
+	} else if (last_dpll_state == ICE_CGU_STATE_LOCKED_HO_ACQ ||
+		   last_dpll_state == ICE_CGU_STATE_HOLDOVER) {
+		*dpll_state = ICE_CGU_STATE_HOLDOVER;
+	}
+	return status;
+}
+
+/**
+ * ice_get_cgu_rclk_pin_info - get info on available recovered clock pins
+ * @hw: pointer to the hw struct
+ * @base_idx: returns index of first recovered clock pin on device
+ * @pin_num: returns number of recovered clock pins available on device
+ *
+ * Based on hw provide caller info about recovery clock pins available on the
+ * board.
+ *
+ * Return:
+ * * 0 - success, information is valid
+ * * negative - failure, information is not valid
+ */
+int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num)
+{
+	int ret;
+
+	switch (hw->device_id) {
+	case ICE_DEV_ID_E810C_SFP:
+	case ICE_DEV_ID_E810C_QSFP:
+		u8 phy_idx;
+
+		ret = ice_get_pf_c827_idx(hw, &phy_idx);
+		if (ret)
+			return ret;
+		*base_idx = E810T_CGU_INPUT_C827(phy_idx, ICE_RCLKA_PIN);
+		*pin_num = ICE_E810_RCLK_PINS_NUM;
+		ret = 0;
+		break;
+	case ICE_DEV_ID_E823L_10G_BASE_T:
+	case ICE_DEV_ID_E823L_1GBE:
+	case ICE_DEV_ID_E823L_BACKPLANE:
+	case ICE_DEV_ID_E823L_QSFP:
+	case ICE_DEV_ID_E823L_SFP:
+	case ICE_DEV_ID_E823C_10G_BASE_T:
+	case ICE_DEV_ID_E823C_BACKPLANE:
+	case ICE_DEV_ID_E823C_QSFP:
+	case ICE_DEV_ID_E823C_SFP:
+	case ICE_DEV_ID_E823C_SGMII:
+		*pin_num = ICE_E822_RCLK_PINS_NUM;
+		ret = 0;
+		if (hw->cgu_part_number ==
+		    ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032)
+			*base_idx = ZL_REF1P;
+		else if (hw->cgu_part_number ==
+			 ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384)
+			*base_idx = SI_REF1P;
+		else
+			ret = -ENODEV;
+
+		break;
+	default:
+		ret = -ENODEV;
+		break;
+	}
+
+	return ret;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index 3b68cb91bd81..855880f026fc 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -3,6 +3,7 @@
 
 #ifndef _ICE_PTP_HW_H_
 #define _ICE_PTP_HW_H_
+#include <uapi/linux/dpll.h>
 
 enum ice_ptp_tmr_cmd {
 	INIT_TIME,
@@ -109,6 +110,232 @@ struct ice_cgu_pll_params_e822 {
 	u32 post_pll_div;
 };
 
+#define E810C_QSFP_C827_0_HANDLE	2
+#define E810C_QSFP_C827_1_HANDLE	3
+enum ice_e810_c827_idx {
+	C827_0,
+	C827_1
+};
+
+enum ice_phy_rclk_pins {
+	ICE_RCLKA_PIN = 0,		/* SCL pin */
+	ICE_RCLKB_PIN,			/* SDA pin */
+};
+
+#define ICE_E810_RCLK_PINS_NUM		(ICE_RCLKB_PIN + 1)
+#define ICE_E822_RCLK_PINS_NUM		(ICE_RCLKA_PIN + 1)
+#define E810T_CGU_INPUT_C827(_phy, _pin) ((_phy) * ICE_E810_RCLK_PINS_NUM + \
+					  (_pin) + ZL_REF1P)
+enum ice_cgu_state {
+	ICE_CGU_STATE_UNKNOWN = -1,
+	ICE_CGU_STATE_INVALID,		/* state is not valid */
+	ICE_CGU_STATE_FREERUN,		/* clock is free-running */
+	ICE_CGU_STATE_LOCKED,		/* clock is locked to the reference,
+					 * but the holdover memory is not valid
+					 */
+	ICE_CGU_STATE_LOCKED_HO_ACQ,	/* clock is locked to the reference
+					 * and holdover memory is valid
+					 */
+	ICE_CGU_STATE_HOLDOVER,		/* clock is in holdover mode */
+	ICE_CGU_STATE_MAX
+};
+
+#define MAX_CGU_STATE_NAME_LEN		14
+struct ice_cgu_state_desc {
+	char name[MAX_CGU_STATE_NAME_LEN];
+	enum ice_cgu_state state;
+};
+
+enum ice_zl_cgu_in_pins {
+	ZL_REF0P = 0,
+	ZL_REF0N,
+	ZL_REF1P,
+	ZL_REF1N,
+	ZL_REF2P,
+	ZL_REF2N,
+	ZL_REF3P,
+	ZL_REF3N,
+	ZL_REF4P,
+	ZL_REF4N,
+	NUM_ZL_CGU_INPUT_PINS
+};
+
+enum ice_zl_cgu_out_pins {
+	ZL_OUT0 = 0,
+	ZL_OUT1,
+	ZL_OUT2,
+	ZL_OUT3,
+	ZL_OUT4,
+	ZL_OUT5,
+	ZL_OUT6,
+	NUM_ZL_CGU_OUTPUT_PINS
+};
+
+enum ice_si_cgu_in_pins {
+	SI_REF0P = 0,
+	SI_REF0N,
+	SI_REF1P,
+	SI_REF1N,
+	SI_REF2P,
+	SI_REF2N,
+	SI_REF3,
+	SI_REF4,
+	NUM_SI_CGU_INPUT_PINS
+};
+
+enum ice_si_cgu_out_pins {
+	SI_OUT0 = 0,
+	SI_OUT1,
+	SI_OUT2,
+	SI_OUT3,
+	SI_OUT4,
+	NUM_SI_CGU_OUTPUT_PINS
+};
+
+#define MAX_CGU_PIN_NAME_LEN		16
+#define ICE_SIG_TYPE_MASK_1PPS_10MHZ	(BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) | \
+					 BIT(DPLL_PIN_SIGNAL_TYPE_10_MHZ))
+struct ice_cgu_pin_desc {
+	char name[MAX_CGU_PIN_NAME_LEN];
+	u8 index;
+	enum dpll_pin_type type;
+	unsigned long sig_type_mask;
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
+	{ "CVL-SDP22",	  ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP20",	  ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "SMA1",	  ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "SMA2/U.FL2",	  ZL_REF3N, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "GNSS-1PPS",	  ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+			BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "MAC-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "CVL-SDP21",	    ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP23",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
+	{ "REF-SMA1",	    ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "PHY2-CLK",	    ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "MAC-CLK",	    ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "CVL-SDP21",	    ZL_OUT5, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "CVL-SDP23",	    ZL_OUT6, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
+	{ "NONE",	  SI_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "NONE",	  SI_REF0N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "SYNCE0_DP",	  SI_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "SYNCE0_DN",	  SI_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	  SI_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_PPS_OUT",  SI_REF3,  DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "INT_PPS_OUT",  SI_REF4,  DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
+	{ "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PHY-CLK",	    SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "10MHZ-SMA2",	    SI_OUT2, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "PPS-SMA1",	    SI_OUT3, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
+	{ "NONE",	  ZL_REF0P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "INT_PPS_OUT",  ZL_REF0N, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "SYNCE0_DP",	  ZL_REF1P, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "SYNCE0_DN",	  ZL_REF1N, DPLL_PIN_TYPE_MUX,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "NONE",	  ZL_REF2P, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "NONE",	  ZL_REF2N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	  ZL_REF3N, DPLL_PIN_TYPE_UNSPEC, 0 },
+	{ "EXT_PPS_OUT",  ZL_REF4P, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "OCXO",	  ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+			BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
+	{ "PPS-SMA1",	   ZL_OUT0, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_1_PPS) },
+	{ "10MHZ-SMA2",	   ZL_OUT1, DPLL_PIN_TYPE_EXT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_10_MHZ) },
+	{ "PHY-CLK",	   ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+		BIT(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) },
+	{ "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
+		ICE_SIG_TYPE_MASK_1PPS_10MHZ },
+	{ "NONE",	   ZL_OUT5, DPLL_PIN_TYPE_UNSPEC, 0 },
+};
+
 extern const struct
 ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];
 
@@ -197,6 +424,19 @@ int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data);
 int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data);
 int ice_read_pca9575_reg_e810t(struct ice_hw *hw, u8 offset, u8 *data);
 bool ice_is_pca9575_present(struct ice_hw *hw);
+bool ice_is_phy_rclk_present(struct ice_hw *hw);
+bool ice_is_clock_mux_present_e810t(struct ice_hw *hw);
+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx);
+bool ice_is_cgu_present(struct ice_hw *hw);
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input);
+unsigned long
+ice_cgu_get_pin_sig_type_mask(struct ice_hw *hw, u8 pin, bool input);
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input);
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+		      enum ice_cgu_state last_dpll_state, u8 *pin,
+		      u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+		      enum ice_cgu_state *dpll_state);
+int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num);
 
 #define PFTSYN_SEM_BYTES	4
 
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index e3f622cad425..c49f573d724f 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -962,6 +962,7 @@ struct ice_hw {
 	DECLARE_BITMAP(hw_ptype, ICE_FLOW_PTYPE_MAX);
 	u8 dvm_ena;
 	u16 io_expander_handle;
+	u8 cgu_part_number;
 };
 
 /* Statistics collected by each port, VSI, VEB, and S-channel */
-- 
2.30.2


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

* [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
  2023-01-17 18:00 ` Vadim Fedorenko
@ 2023-01-17 18:00   ` Vadim Fedorenko
  -1 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, linux-arm-kernel, linux-clk, Milena Olech,
	Michal Michalik, Vadim Fedorenko

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

Control over clock generation unit is required for further development
of Synchronous Ethernet feature. Interface provides ability to obtain
current state of a dpll, its sources and outputs which are pins, and
allows their configuration.

Co-developed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Co-developed-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
---
 drivers/net/ethernet/intel/Kconfig        |    1 +
 drivers/net/ethernet/intel/ice/Makefile   |    3 +-
 drivers/net/ethernet/intel/ice/ice.h      |    4 +
 drivers/net/ethernet/intel/ice/ice_dpll.c | 2115 +++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h |   99 +
 drivers/net/ethernet/intel/ice/ice_main.c |   10 +
 6 files changed, 2231 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h

diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 3facb55b7161..dcf0e12991bf 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -300,6 +300,7 @@ config ICE
 	select DIMLIB
 	select NET_DEVLINK
 	select PLDMFW
+	select DPLL
 	help
 	  This driver supports Intel(R) Ethernet Connection E800 Series of
 	  devices.  For more information on how to identify your adapter, go
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 9183d480b70b..ebfd456f76cd 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -32,7 +32,8 @@ ice-y := ice_main.o	\
 	 ice_lag.o	\
 	 ice_ethtool.o  \
 	 ice_repr.o	\
-	 ice_tc_lib.o
+	 ice_tc_lib.o	\
+	 ice_dpll.o
 ice-$(CONFIG_PCI_IOV) +=	\
 	ice_sriov.o		\
 	ice_virtchnl.o		\
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 3368dba42789..efc1844ef77c 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -73,6 +73,7 @@
 #include "ice_lag.h"
 #include "ice_vsi_vlan_ops.h"
 #include "ice_gnss.h"
+#include "ice_dpll.h"
 
 #define ICE_BAR0		0
 #define ICE_REQ_DESC_MULTIPLE	32
@@ -198,6 +199,7 @@
 enum ice_feature {
 	ICE_F_DSCP,
 	ICE_F_PTP_EXTTS,
+	ICE_F_PHY_RCLK,
 	ICE_F_SMA_CTRL,
 	ICE_F_CGU,
 	ICE_F_GNSS,
@@ -509,6 +511,7 @@ enum ice_pf_flags {
 	ICE_FLAG_PLUG_AUX_DEV,
 	ICE_FLAG_MTU_CHANGED,
 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
 	ICE_PF_FLAGS_NBITS		/* must be last */
 };
 
@@ -633,6 +636,7 @@ struct ice_pf {
 #define ICE_VF_AGG_NODE_ID_START	65
 #define ICE_MAX_VF_AGG_NODES		32
 	struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
+	struct ice_dplls dplls;
 };
 
 struct ice_netdev_priv {
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
new file mode 100644
index 000000000000..6ed1fcee4e03
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,2115 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022, Intel Corporation. */
+
+#include "ice.h"
+#include "ice_lib.h"
+#include "ice_trace.h"
+#include <linux/dpll.h>
+#include <uapi/linux/dpll.h>
+
+#define CGU_STATE_ACQ_ERR_THRESHOLD	50
+#define ICE_DPLL_FREQ_1_HZ		1
+#define ICE_DPLL_FREQ_10_MHZ		10000000
+
+/**
+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock status
+ */
+static const enum dpll_lock_status
+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
+};
+
+/**
+ * ice_dpll_pin_type - enumerate ince pin types
+ */
+enum ice_dpll_pin_type {
+	ICE_DPLL_PIN_INVALID = 0,
+	ICE_DPLL_PIN_TYPE_SOURCE,
+	ICE_DPLL_PIN_TYPE_OUTPUT,
+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
+};
+
+/**
+ * pin_type_name - string names of ice pin types
+ */
+static const char * const pin_type_name[] = {
+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
+};
+
+/**
+ * ice_dpll_pin_signal_type_to_freq - translate pin_signal_type to freq value
+ * @sig_type: signal type to find frequency
+ * @freq: on success - frequency of a signal type
+ *
+ * Return:
+ * * 0 - frequency valid
+ * * negative - error
+ */
+static inline int
+ice_dpll_pin_signal_type_to_freq(const enum dpll_pin_signal_type sig_type,
+				 u32 *freq)
+{
+	if (sig_type == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
+		return -EINVAL;
+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_1_PPS)
+		*freq = ICE_DPLL_FREQ_1_HZ;
+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_10_MHZ)
+		*freq = ICE_DPLL_FREQ_10_MHZ;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_pin_freq_to_signal_type - translate pin freq to signal type
+ * @freq: frequency to translate
+ *
+ * Return: signal type of a pin based on frequency.
+ */
+static inline enum dpll_pin_signal_type
+ice_dpll_pin_freq_to_signal_type(u32 freq)
+{
+	if (freq == ICE_DPLL_FREQ_1_HZ)
+		return DPLL_PIN_SIGNAL_TYPE_1_PPS;
+	else if (freq == ICE_DPLL_FREQ_10_MHZ)
+		return DPLL_PIN_SIGNAL_TYPE_10_MHZ;
+	else
+		return DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ;
+}
+
+/**
+ * ice_dpll_pin_signal_type_set - set pin's signal type in hardware
+ * @pf: Board private structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being configured
+ * @sig_type: signal type to be set
+ *
+ * Translate pin signal type to frequency and set it on a pin.
+ *
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+__ice_dpll_pin_signal_type_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			       const enum ice_dpll_pin_type pin_type,
+			       const enum dpll_pin_signal_type sig_type)
+{
+	u8 flags;
+	u32 freq;
+	int ret;
+
+	ret = ice_dpll_pin_signal_type_to_freq(sig_type, &freq);
+	if (ret)
+		return ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
+					       pin->flags, freq, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags = pin->flags | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
+						0, freq, 0);
+	} else {
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		dev_dbg(ice_pf_to_dev(pf),
+			"err:%d %s failed to set pin freq:%u on pin:%u\n",
+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
+			freq, pin->idx);
+	} else {
+		pin->signal_type = sig_type;
+		pin->freq = freq;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_enable - enable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being enabled
+ *
+ * Enable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		    const enum ice_dpll_pin_type pin_type)
+{
+	u8 flags = pin->flags;
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		flags |= ICE_DPLL_RCLK_SOURCE_FLAG_EN;
+		ret = 0;
+	}
+	if (ret)
+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+			"err:%d %s failed to enable %s pin:%u\n",
+			ret, ice_aq_str(hw->adminq.sq_last_status),
+			pin_type_name[pin_type], pin->idx);
+	else
+		pin->flags = flags;
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_disable - disable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being disabled
+ *
+ * Disable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		     enum ice_dpll_pin_type pin_type)
+{
+	u8 flags = pin->flags;
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		flags &= ~(ICE_DPLL_RCLK_SOURCE_FLAG_EN);
+		ret = ice_aq_set_phy_rec_clk_out(hw, pin->idx, false,
+						 &pin->freq);
+	}
+
+	if (ret)
+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+			"err:%d %s failed to disable %s pin:%u\n",
+			ret, ice_aq_str(hw->adminq.sq_last_status),
+			pin_type_name[pin_type], pin->idx);
+	else
+		pin->flags = flags;
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_update - update pin's mode
+ * @hw: private board struct
+ * @pin: structure with pin attributes to be updated
+ * @pin_type: type of pin being updated
+ *
+ * Determine pin current mode, frequency and signal type. Then update struct
+ * holding the pin info.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+int
+ice_dpll_pin_update(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		    const enum ice_dpll_pin_type pin_type)
+{
+	int ret;
+
+	pin->mode_mask = 0;
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		ret = ice_aq_get_input_pin_cfg(hw, pin->idx, NULL, NULL, NULL,
+					       &pin->flags, &pin->freq, NULL);
+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags)
+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
+		else
+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		ret = ice_aq_get_output_pin_cfg(hw, pin->idx, &pin->flags,
+						NULL, &pin->freq, NULL);
+		set_bit(DPLL_PIN_MODE_OUTPUT, &pin->mode_mask);
+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags)
+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
+		else
+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
+		if (ICE_DPLL_RCLK_SOURCE_FLAG_EN & pin->flags)
+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
+		else
+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
+		ret = 0;
+	}
+	pin->signal_type = ice_dpll_pin_freq_to_signal_type(pin->freq);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_mode_set - set pin's mode
+ * @pf: Board private structure
+ * @pin: pointer to a pin
+ * @pin_type: type of modified pin
+ * @mode: requested mode
+ *
+ * Determine requested pin mode set it on a pin.
+ *
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+ice_dpll_pin_mode_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
+		      const enum ice_dpll_pin_type pin_type,
+		      const enum dpll_pin_mode mode)
+{
+	int ret;
+
+	if (!test_bit(mode, &pin->mode_supported_mask))
+		return -EINVAL;
+
+	if (test_bit(mode, &pin->mode_mask))
+		return 0;
+
+	if (mode == DPLL_PIN_MODE_CONNECTED)
+		ret = ice_dpll_pin_enable(&pf->hw, pin, pin_type);
+	else if (mode == DPLL_PIN_MODE_DISCONNECTED)
+		ret = ice_dpll_pin_disable(&pf->hw, pin, pin_type);
+	else
+		ret = -EINVAL;
+
+	if (!ret)
+		ret = ice_dpll_pin_update(&pf->hw, pin, pin_type);
+
+	return ret;
+}
+
+/**
+ * ice_find_dpll - find ice_dpll on a pf
+ * @pf: private board structure
+ * @dpll: kernel's dpll_device pointer to be searched
+ *
+ * Return:
+ * * pointer if ice_dpll with given device dpll pointer is found
+ * * NULL if not found
+ */
+static struct ice_dpll
+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
+{
+	if (!pf || !dpll)
+		return NULL;
+
+	return (dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
+		dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL);
+}
+
+/**
+ * ice_find_pin - find ice_dpll_pin on a pf
+ * @pf: private board structure
+ * @pin: kernel's dpll_pin pointer to be searched for
+ * @pin_type: type of pins to be searched for
+ *
+ * Find and return internal ice pin info pointer holding data of given dpll subsystem
+ * pin pointer.
+ *
+ * Return:
+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer was
+ * found in pf internal pin data.
+ * * NULL - if pin was not found.
+ */
+static struct ice_dpll_pin
+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
+	      enum ice_dpll_pin_type pin_type)
+
+{
+	struct ice_dpll_pin *pins;
+	int pin_num, i;
+
+	if (!pin || !pf)
+		return NULL;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		pins = pf->dplls.inputs;
+		pin_num = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		pins = pf->dplls.outputs;
+		pin_num = pf->dplls.num_outputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		pins = pf->dplls.rclk;
+		pin_num = pf->dplls.num_rclk;
+	} else {
+		return NULL;
+	}
+
+	for (i = 0; i < pin_num; i++)
+		if (pin == pins[i].pin)
+			return &pins[i];
+
+	return NULL;
+}
+
+/**
+ * ice_dpll_hw_source_prio_set - set source priority value in hardware
+ * @pf: board private structure
+ * @dpll: ice dpll pointer
+ * @pin: ice pin pointer
+ * @prio: priority value being set on a dpll
+ *
+ * Internal wrapper for setting the priority in the hardware.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_hw_source_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
+			    struct ice_dpll_pin *pin, const u32 prio)
+{
+	int ret;
+
+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
+				      (u8)prio);
+	if (ret)
+		dev_dbg(ice_pf_to_dev(pf),
+			"err:%d %s failed to set pin prio:%u on pin:%u\n",
+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
+			prio, pin->idx);
+	else
+		dpll->input_prio[pin->idx] = prio;
+
+	return ret;
+}
+
+/**
+ * ice_dpll_lock_status_get - get dpll lock status callback
+ * @dpll: registered dpll pointer
+ * @status: on success holds dpll's lock status
+ *
+ * Dpll subsystem callback, provides dpll's lock status.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_lock_status_get(const struct dpll_device *dpll,
+				    enum dpll_lock_status *status)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		return -EFAULT;
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
+	mutex_lock(&pf->dplls.lock);
+	*status = ice_dpll_status[d->dpll_state];
+	mutex_unlock(&pf->dplls.lock);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_source_idx_get - get dpll's source index
+ * @dpll: registered dpll pointer
+ * @pin_idx: on success holds currently selected source pin index
+ *
+ * Dpll subsystem callback. Provides index of a source dpll is trying to lock
+ * with.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_idx_get(const struct dpll_device *dpll, u32 *pin_idx)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+	int ret = 0;
+
+	mutex_lock(&pf->dplls.lock);
+	d = ice_find_dpll(pf, dpll);
+	if (!d) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	*pin_idx = (u32)d->source_idx;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p d:%p, idx:%u\n",
+		__func__, dpll, pf, d, *pin_idx);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_mode_get - get dpll's working mode
+ * @dpll: registered dpll pointer
+ * @mode: on success holds current working mode of dpll
+ *
+ * Dpll subsystem callback. Provides working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_get(const struct dpll_device *dpll,
+			     enum dpll_mode *mode)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+	int ret = 0;
+
+	mutex_lock(&pf->dplls.lock);
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		ret = -EFAULT;
+	else
+		*mode = DPLL_MODE_AUTOMATIC;
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_mode_get - check if dpll's working mode is supported
+ * @dpll: registered dpll pointer
+ * @mode: mode to be checked for support
+ *
+ * Dpll subsystem callback. Provides information if working mode is supported
+ * by dpll.
+ *
+ * Return:
+ * * true - mode is supported
+ * * false - mode is not supported
+ */
+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
+				    const enum dpll_mode mode)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+	bool ret = true;
+
+	mutex_lock(&pf->dplls.lock);
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		ret = false;
+	else
+		if (mode != DPLL_MODE_AUTOMATIC)
+			ret = false;
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_signal_type_supported - if pin signal type is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type being checked for support
+ * @pin_type: type of a pin being checked
+ *
+ * Check is signal type is supported on given pin/dpll pair.
+ *
+ * Return:
+ * * true - supported
+ * * false - not supported
+ */
+static bool
+ice_dpll_pin_signal_type_supported(const struct dpll_device *dpll,
+				   const struct dpll_pin *pin,
+				   const enum dpll_pin_signal_type sig_type,
+				   const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	bool supported = false;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (p) {
+		if (test_bit(sig_type, &p->signal_type_mask))
+			supported = true;
+	}
+	mutex_unlock(&pf->dplls.lock);
+
+	return supported;
+}
+
+/**
+ * ice_dpll_rclk_signal_type_supported - if rclk source signal type is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type being checked for support
+ *
+ * Dpll subsystem callback. Check is signal type is supported on given pin/dpll
+ * pair.
+ *
+ * Return:
+ * * true - supported
+ * * false - not supported
+ */
+static bool
+ice_dpll_rclk_signal_type_supported(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin,
+				    const enum dpll_pin_signal_type sig_type)
+{
+	const enum ice_dpll_pin_type t = ICE_DPLL_PIN_TYPE_RCLK_SOURCE;
+
+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type, t);
+}
+
+/**
+ * ice_dpll_source_signal_type_supported - if source signal type is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type being checked for support
+ *
+ * Dpll subsystem callback. Check is signal type is supported on given pin/dpll
+ * pair.
+ *
+ * Return:
+ * * true - supported
+ * * false - not supported
+ */
+static bool
+ice_dpll_source_signal_type_supported(const struct dpll_device *dpll,
+				      const struct dpll_pin *pin,
+				      const enum dpll_pin_signal_type sig_type)
+{
+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
+						  ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_output_signal_type_supported - if output pin signal type is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type being checked
+ *
+ * Dpll subsystem callback. Check if signal type is supported on given pin/dpll
+ * pair.
+ *
+ * Return:
+ * * true - supported
+ * * false - not supported
+ */
+static bool
+ice_dpll_output_signal_type_supported(const struct dpll_device *dpll,
+				      const struct dpll_pin *pin,
+				      const enum dpll_pin_signal_type sig_type)
+{
+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
+						  ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_signal_type_get - get dpll's pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: on success - current signal type used on the pin
+ * @pin_type: type of a pin being checked
+ *
+ * Find a pin and assign sig_type with its current signal type value.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_pin_signal_type_get(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					enum dpll_pin_signal_type *sig_type,
+					const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	int ret = -ENODEV;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (p) {
+		*sig_type = p->signal_type;
+		ret = 0;
+	}
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p\n",
+		__func__, dpll, pin, pf, p, p ? p->pin : NULL);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_output_signal_type_get - get dpll's output pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: on success - current signal type value used on the pin
+ *
+ * Dpll subsystem callback. Find current signal type of given pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_output_signal_type_get(const struct dpll_device *dpll,
+					   const struct dpll_pin *pin,
+					   enum dpll_pin_signal_type *sig_type)
+{
+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
+					    ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_signal_type_set - set dpll pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type to be set
+ * @pin_type: type of a pin being configured
+ *
+ * Handler for signal type modification on pins. Set signal type value for
+ * a given pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_pin_signal_type_set(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					const enum dpll_pin_signal_type sig_type,
+					const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	int ret = -EFAULT;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p)
+		goto unlock;
+	if (test_bit(sig_type, &p->signal_type_mask))
+		ret = __ice_dpll_pin_signal_type_set(pf, p, pin_type, sig_type);
+	else
+		ret = -EINVAL;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
+		__func__, dpll, pin, pf, p, p ? p->pin : NULL,  ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_output_signal_type_set - set dpll output pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type to be set
+ *
+ * Dpll subsystem callback. Wraps signal type modification handler.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_output_signal_type_set(const struct dpll_device *dpll,
+				const struct dpll_pin *pin,
+				const enum dpll_pin_signal_type sig_type)
+{
+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
+					    ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_mode_enable - enables a pin mode
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be set
+ * @pin_type: type of pin being modified
+ *
+ * Handler for enabling the pin mode.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_pin_mode_enable(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin,
+				    const enum dpll_pin_mode mode,
+				    const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	int ret = -EFAULT;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (p)
+		ret = ice_dpll_pin_mode_set(pf, p, pin_type, mode);
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
+		__func__, dpll, pin, pf, p, p ? p->pin : NULL, ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_mode_enable - enable rclk-source pin mode
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be set
+ *
+ * Dpll subsystem callback. Enables given mode on recovered clock source type
+ * pin.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_rclk_mode_enable(const struct dpll_device *dpll,
+				     const struct dpll_pin *pin,
+				     const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * ice_dpll_output_mode_enable - enable output pin mode
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be set
+ *
+ * Dpll subsystem callback. Enables given mode on output type pin.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_output_mode_enable(const struct dpll_device *dpll,
+				       const struct dpll_pin *pin,
+				       const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
+					 ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_mode_enable - enable source pin mode
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be set
+ *
+ * Dpll subsystem callback. Enables given mode on source type pin.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_source_mode_enable(const struct dpll_device *dpll,
+				       const struct dpll_pin *pin,
+				       const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
+					 ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_signal_type_get - get source pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @type: on success - source pin signal type
+ *
+ * Dpll subsystem callback. Get source pin signal type value.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_signal_type_get(const struct dpll_device *dpll,
+					   const struct dpll_pin *pin,
+					   enum dpll_pin_signal_type *sig_type)
+{
+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
+					    ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_signal_type_set - set dpll output pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type to be set
+ *
+ * dpll subsystem callback. Set source pin signal type value.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_source_signal_type_set(const struct dpll_device *dpll,
+				const struct dpll_pin *pin,
+				const enum dpll_pin_signal_type sig_type)
+{
+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
+					    ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_prio_get - get dpll's source prio
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @prio: on success - returns source priority on dpll
+ *
+ * Dpll subsystem callback. Handler for getting priority of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_prio_get(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin, u32 *prio)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll *d = NULL;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (*prio > ICE_DPLL_PRIO_MAX)
+		return ret;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (!p)
+		goto unlock;
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		goto unlock;
+	*prio = d->input_prio[p->idx];
+	ret = 0;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
+		__func__, dpll, pin, pf, ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_source_prio_set - set dpll source prio
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @prio: source priority to be set on dpll
+ *
+ * Dpll subsystem callback. Handler for setting priority of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_prio_set(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin, const u32 prio)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll *d = NULL;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (prio > ICE_DPLL_PRIO_MAX)
+		return ret;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (!p)
+		goto unlock;
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		goto unlock;
+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
+		__func__, dpll, pin, pf, ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_mode_active -  check if given pin's mode is active
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ * @pin_type: type of a pin to be checked
+ *
+ * Handler for checking if given mode is active on a pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_pin_mode_active(const struct dpll_device *dpll,
+				     const struct dpll_pin *pin,
+				     const enum dpll_pin_mode mode,
+				     const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	bool ret = false;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p)
+		goto unlock;
+	if (test_bit(mode, &p->mode_mask))
+		ret = true;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_mode_active - check if rclk source pin's mode is active
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
+ * on a recovered clock pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_rclk_mode_active(const struct dpll_device *dpll,
+				      const struct dpll_pin *pin,
+				      const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_active(dpll, pin, mode,
+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * ice_dpll_output_mode_active - check if output pin's mode is active
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
+ * on an output pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_output_mode_active(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_active(dpll, pin, mode,
+					ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_mode_active - check if source pin's mode is active
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
+ * on a source pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_source_mode_active(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_active(dpll, pin, mode,
+					ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_pin_mode_supported - check if pin's mode is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ * @pin_type: type of a pin being checked
+ *
+ * Handler for checking if given mode is supported on a pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_pin_mode_supported(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					const enum dpll_pin_mode mode,
+					const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	bool ret = false;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+
+	if (!p)
+		goto unlock;
+	if (test_bit(mode, &p->mode_supported_mask))
+		ret = true;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_mode_supported - check if rclk pin's mode is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is
+ * supported on a clock recovery pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_rclk_mode_supported(const struct dpll_device *dpll,
+					 const struct dpll_pin *pin,
+					 const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
+					    ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * ice_dpll_output_mode_supported - check if output pin's mode is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is
+ * supported on an output pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_output_mode_supported(const struct dpll_device *dpll,
+					   const struct dpll_pin *pin,
+					   const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
+					    ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_mode_supported - check if source pin's mode is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is
+ * supported on a source pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_source_mode_supported(const struct dpll_device *dpll,
+					   const struct dpll_pin *pin,
+					   const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
+					    ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_rclk_pin_sig_type_get - get signal type of rclk pin
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: on success - holds a signal type of a pin
+ *
+ * DPLL subsystem callback, provides signal type of clock recovery pin.
+ *
+ * Return:
+ * * 0 - success, sig_type value is valid
+ * * negative - error
+ */
+static int ice_dpll_rclk_pin_sig_type_get(const struct dpll_device *dpll,
+					  const struct dpll_pin *pin,
+					  enum dpll_pin_signal_type *sig_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	int ret = 0;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (!p) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	*sig_type = p->signal_type;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p sig_type:%d ret:%d\n",
+		__func__, dpll, pin, pf, *sig_type, ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_pin_net_if_index_get - get OS interface index callback
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @net_if_idx: on success - holds OS interface index
+ *
+ * dpll subsystem callback, obtains OS interface index and pass to the caller.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_rclk_pin_net_if_index_get(const struct dpll_device *dpll,
+					      const struct dpll_pin *pin,
+					      int *net_if_idx)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+
+	if (!pf->vsi[0] || pf->vsi[0]->netdev)
+		return -EAGAIN;
+	*net_if_idx = pf->vsi[0]->netdev->ifindex;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_pin_select - select a recovered clock pin as a valid source
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ *
+ * dpll subsystem callback, selects a pin for clock recovery,
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_rclk_pin_select(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	u32 freq;
+	int ret;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (!p) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	if (!(p->flags & ICE_DPLL_RCLK_SOURCE_FLAG_EN)) {
+		ret = -EPERM;
+		goto unlock;
+	}
+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, p->idx, true, &freq);
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
+		__func__, dpll, pin, pf, ret);
+
+	return ret;
+}
+
+static struct dpll_pin_ops ice_dpll_rclk_ops = {
+	.mode_enable = ice_dpll_rclk_mode_enable,
+	.mode_active = ice_dpll_rclk_mode_active,
+	.mode_supported = ice_dpll_rclk_mode_supported,
+	.signal_type_get = ice_dpll_rclk_pin_sig_type_get,
+	.signal_type_supported = ice_dpll_rclk_signal_type_supported,
+	.net_if_idx_get = ice_dpll_rclk_pin_net_if_index_get,
+	.select = ice_dpll_rclk_pin_select,
+};
+
+static struct dpll_pin_ops ice_dpll_source_ops = {
+	.signal_type_get = ice_dpll_source_signal_type_get,
+	.signal_type_set = ice_dpll_source_signal_type_set,
+	.signal_type_supported = ice_dpll_source_signal_type_supported,
+	.mode_active = ice_dpll_source_mode_active,
+	.mode_enable = ice_dpll_source_mode_enable,
+	.mode_supported = ice_dpll_source_mode_supported,
+	.prio_get = ice_dpll_source_prio_get,
+	.prio_set = ice_dpll_source_prio_set,
+};
+
+static struct dpll_pin_ops ice_dpll_output_ops = {
+	.signal_type_get = ice_dpll_output_signal_type_get,
+	.signal_type_set = ice_dpll_output_signal_type_set,
+	.signal_type_supported = ice_dpll_output_signal_type_supported,
+	.mode_active = ice_dpll_output_mode_active,
+	.mode_enable = ice_dpll_output_mode_enable,
+	.mode_supported = ice_dpll_output_mode_supported,
+};
+
+static struct dpll_device_ops ice_dpll_ops = {
+	.lock_status_get = ice_dpll_lock_status_get,
+	.source_pin_idx_get = ice_dpll_source_idx_get,
+	.mode_get = ice_dpll_mode_get,
+	.mode_supported = ice_dpll_mode_supported,
+};
+
+/**
+ * ice_dpll_release_info - release memory allocated for pins
+ * @pf: board private structure
+ *
+ * Release memory allocated for pins by ice_dpll_init_info function.
+ */
+static void ice_dpll_release_info(struct ice_pf *pf)
+{
+	kfree(pf->dplls.inputs);
+	pf->dplls.inputs = NULL;
+	kfree(pf->dplls.outputs);
+	pf->dplls.outputs = NULL;
+	kfree(pf->dplls.eec.input_prio);
+	pf->dplls.eec.input_prio = NULL;
+	kfree(pf->dplls.pps.input_prio);
+	pf->dplls.pps.input_prio = NULL;
+}
+
+/**
+ * ice_dpll_init_pins - initializes source or output pins information
+ * @pf: Board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Init information about input or output pins, cache them in pins struct.
+ */
+static int ice_dpll_init_pins(struct ice_pf *pf,
+			      const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
+	int ret = -EINVAL, num_pins, i;
+	struct ice_hw *hw = &pf->hw;
+	struct ice_dpll_pin *pins;
+	bool input;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		input = true;
+		pins = pf->dplls.inputs;
+		num_pins = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		input = false;
+		pins = pf->dplls.outputs;
+		num_pins = pf->dplls.num_outputs;
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_pins; i++) {
+		pins[i].idx = i;
+		pins[i].name = ice_cgu_get_pin_name(hw, i, input);
+		pins[i].type = ice_cgu_get_pin_type(hw, i, input);
+		set_bit(DPLL_PIN_MODE_CONNECTED,
+			&pins[i].mode_supported_mask);
+		set_bit(DPLL_PIN_MODE_DISCONNECTED,
+			&pins[i].mode_supported_mask);
+		if (input) {
+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
+						      &de->input_prio[i]);
+			if (ret)
+				return ret;
+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
+						      &dp->input_prio[i]);
+			if (ret)
+				return ret;
+			set_bit(DPLL_PIN_MODE_SOURCE,
+				&pins[i].mode_supported_mask);
+		} else {
+			set_bit(DPLL_PIN_MODE_OUTPUT,
+				&pins[i].mode_supported_mask);
+		}
+		pins[i].signal_type_mask =
+				ice_cgu_get_pin_sig_type_mask(hw, i, input);
+		ret = ice_dpll_pin_update(hw, &pins[i], pin_type);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
+ * @dpll_eec: dpll_eec dpll pointer
+ * @dpll_pps: dpll_pps dpll pointer
+ * @pins: pointer to pins array
+ * @count: number of pins
+ *
+ * Deregister and free pins of a given array of pins from dpll devices registered
+ * in dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * positive - number of errors encounterd on pin's deregistration.
+ */
+static int
+ice_dpll_release_pins(struct dpll_device *dpll_eec,
+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
+		      int count)
+{
+	int i, ret, err;
+
+	for (i = 0; i < count; i++) {
+		struct ice_dpll_pin *p = &pins[i];
+
+		if (p && p->pin) {
+			if (dpll_eec) {
+				ret = dpll_pin_deregister(dpll_eec, p->pin);
+				if (ret)
+					err++;
+			}
+			if (dpll_pps) {
+				ret = dpll_pin_deregister(dpll_pps, p->pin);
+				if (ret)
+					err++;
+			}
+			dpll_pin_free(p->pin);
+			p->pin = NULL;
+		}
+	}
+
+	return err;
+}
+
+/**
+ * ice_dpll_register_pins - register pins with a dpll
+ * @pf: board private structure
+ * @dpll: registered dpll pointer
+ * @pin_type: type of pins being registered
+ *
+ * Register source or output pins within given DPLL in a Linux dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_register_pins(struct ice_pf *pf, struct dpll_device *dpll,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *pins;
+	struct dpll_pin_ops *ops;
+	int ret, i, count;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		ops = &ice_dpll_source_ops;
+		pins = pf->dplls.inputs;
+		count = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		ops = &ice_dpll_output_ops;
+		pins = pf->dplls.outputs;
+		count = pf->dplls.num_outputs;
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < count; i++) {
+		pins[i].pin = dpll_pin_alloc(pins[i].name, pins[i].type);
+		if (IS_ERR_OR_NULL(pins[i].pin))
+			return -ENOMEM;
+
+		ret = dpll_pin_register(dpll, pins[i].pin, ops, pf);
+		if (ret)
+			return -ENOSPC;
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_register_shared_pins - register shared pins in DPLL subsystem
+ * @pf: board private structure
+ * @dpll_o: registered dpll pointer (owner)
+ * @dpll: registered dpll pointer
+ * @type: type of pins being registered
+ *
+ * Register pins from given owner dpll within given dpll in Linux dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_register_shared_pins(struct ice_pf *pf, struct dpll_device *dpll_o,
+			      struct dpll_device *dpll,
+			      const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *pins;
+	struct dpll_pin_ops *ops;
+	int ret, i, count;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		ops = &ice_dpll_source_ops;
+		pins = pf->dplls.inputs;
+		count = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		ops = &ice_dpll_output_ops;
+		pins = pf->dplls.outputs;
+		count = pf->dplls.num_outputs;
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < count; i++) {
+		ret = dpll_shared_pin_register(dpll_o, dpll, pins[i].name,
+					       ops, pf);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_init_info - prepare pf's dpll information structure
+ * @pf: board private structure
+ *
+ * Acquire (from HW) and set basic dpll information (on pf->dplls struct).
+ *
+ * Return:
+ *  0 - success
+ *  negative - error
+ */
+static int ice_dpll_init_info(struct ice_pf *pf)
+{
+	struct ice_aqc_get_cgu_abilities abilities;
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_hw *hw = &pf->hw;
+	int ret, alloc_size;
+
+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
+	if (ret) {
+		dev_err(ice_pf_to_dev(pf),
+			"err:%d %s failed to read cgu abilities\n",
+			ret, ice_aq_str(hw->adminq.sq_last_status));
+		return ret;
+	}
+
+	de->dpll_idx = abilities.eec_dpll_idx;
+	dp->dpll_idx = abilities.pps_dpll_idx;
+	d->num_inputs = abilities.num_inputs;
+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
+	if (!d->inputs)
+		return -ENOMEM;
+
+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+	if (!de->input_prio)
+		return -ENOMEM;
+
+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+	if (!dp->input_prio)
+		return -ENOMEM;
+
+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (ret)
+		goto release_info;
+
+	d->num_outputs = abilities.num_outputs;
+	alloc_size = sizeof(*d->outputs) * d->num_outputs;
+	d->outputs = kzalloc(alloc_size, GFP_KERNEL);
+	if (!d->outputs)
+		goto release_info;
+
+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
+	if (ret)
+		goto release_info;
+
+	dev_dbg(ice_pf_to_dev(pf), "%s - success, inputs:%u, outputs:%u\n", __func__,
+		abilities.num_inputs, abilities.num_outputs);
+
+	return 0;
+
+release_info:
+	dev_err(ice_pf_to_dev(pf),
+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
+		__func__, d->inputs, de->input_prio,
+		dp->input_prio, d->outputs);
+	ice_dpll_release_info(pf);
+	return ret;
+}
+
+/**
+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
+ * @pf: board private structure
+ * @clock_id: holds generated clock_id
+ *
+ * Generates unique (per board) clock_id for allocation and search of dpll
+ * devices in Linux dpll subsystem.
+ */
+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
+{
+	*clock_id = pci_get_dsn(pf->pdev);
+}
+
+/**
+ * ice_dpll_init_dpll
+ * @pf: board private structure
+ *
+ * Allocate and register dpll in dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - allocation fails
+ */
+static int ice_dpll_init_dpll(struct ice_pf *pf)
+{
+	struct device *dev = ice_pf_to_dev(pf);
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	u64 clock_id = 0;
+	int ret = 0;
+
+	ice_generate_clock_id(pf, &clock_id);
+
+	de->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_EEC,
+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
+	if (!de->dpll) {
+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (eec)\n");
+		return -ENOMEM;
+	}
+
+	dp->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_PPS,
+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
+	if (!dp->dpll) {
+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (pps)\n");
+		return -ENOMEM;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_update_state
+ * @hw: board private structure
+ * @d: pointer to queried dpll device
+ *
+ * Poll current state of dpll from hw and update ice_dpll struct.
+ * Return:
+ * * 0 - success
+ * * negative - AQ failure
+ */
+static int ice_dpll_update_state(struct ice_hw *hw, struct ice_dpll *d)
+{
+	int ret;
+
+	ret = ice_get_cgu_state(hw, d->dpll_idx, d->prev_dpll_state,
+				&d->source_idx, &d->ref_state, &d->eec_mode,
+				&d->phase_offset, &d->dpll_state);
+
+	dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+		"update dpll=%d, src_idx:%u, state:%d, prev:%d\n",
+		d->dpll_idx, d->source_idx,
+		d->dpll_state, d->prev_dpll_state);
+
+	if (ret)
+		dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+			"update dpll=%d state failed, ret=%d %s\n",
+			d->dpll_idx, ret,
+			ice_aq_str(hw->adminq.sq_last_status));
+
+	return ret;
+}
+
+/**
+ * ice_dpll_notify_changes - notify dpll subsystem about changes
+ * @d: pointer do dpll
+ *
+ * Once change detected appropriate event is submitted to the dpll subsystem.
+ */
+static void ice_dpll_notify_changes(struct ice_dpll *d)
+{
+	if (d->prev_dpll_state != d->dpll_state) {
+		d->prev_dpll_state = d->dpll_state;
+		dpll_device_notify(d->dpll, DPLL_CHANGE_LOCK_STATUS);
+	}
+	if (d->prev_source_idx != d->source_idx) {
+		d->prev_source_idx = d->source_idx;
+		dpll_device_notify(d->dpll, DPLL_CHANGE_SOURCE_PIN);
+	}
+}
+
+/**
+ * ice_dpll_periodic_work - DPLLs periodic worker
+ * @work: pointer to kthread_work structure
+ *
+ * DPLLs periodic worker is responsible for polling state of dpll.
+ */
+static void ice_dpll_periodic_work(struct kthread_work *work)
+{
+	struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	int ret = 0;
+
+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
+		return;
+	mutex_lock(&d->lock);
+	ret = ice_dpll_update_state(&pf->hw, de);
+	if (!ret)
+		ret = ice_dpll_update_state(&pf->hw, dp);
+	if (ret) {
+		d->cgu_state_acq_err_num++;
+		/* stop rescheduling this worker */
+		if (d->cgu_state_acq_err_num >
+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
+			dev_err(ice_pf_to_dev(pf),
+				"EEC/PPS DPLLs periodic work disabled\n");
+			return;
+		}
+	}
+	mutex_unlock(&d->lock);
+	ice_dpll_notify_changes(de);
+	ice_dpll_notify_changes(dp);
+
+	/* Run twice a second or reschedule if update failed */
+	kthread_queue_delayed_work(d->kworker, &d->work,
+				   ret ? msecs_to_jiffies(10) :
+				   msecs_to_jiffies(500));
+}
+
+/**
+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
+ * @pf: board private structure
+ *
+ * Create and start DPLLs periodic worker.
+ * Return:
+ * * 0 - success
+ * * negative - create worker failure
+ */
+static int ice_dpll_init_worker(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	struct kthread_worker *kworker;
+
+	ice_dpll_update_state(&pf->hw, &d->eec);
+	ice_dpll_update_state(&pf->hw, &d->pps);
+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
+	kworker = kthread_create_worker(0, "ice-dplls-%s",
+					dev_name(ice_pf_to_dev(pf)));
+	if (IS_ERR(kworker))
+		return PTR_ERR(kworker);
+	d->kworker = kworker;
+	d->cgu_state_acq_err_num = 0;
+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
+
+	return 0;
+}
+
+/**
+ * __ice_dpll_release - Disable the driver/HW support for DPLL and unregister
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * This function handles the cleanup work required from the initialization by
+ * freeing resources and unregistering the dpll.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void __ice_dpll_release(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_dpll *de = &d->eec;
+	struct ice_dpll *dp = &d->pps;
+	int ret;
+
+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
+				    d->num_inputs);
+	if (ret)
+		dev_warn(ice_pf_to_dev(pf),
+			 "pin deregister on PPS dpll err=%d\n", ret);
+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
+				    d->num_outputs);
+	if (ret)
+		dev_warn(ice_pf_to_dev(pf),
+			 "pin deregister on PPS dpll err=%d\n", ret);
+	ice_dpll_release_info(pf);
+	if (dp->dpll) {
+		dpll_device_unregister(dp->dpll);
+		dpll_device_free(dp->dpll);
+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
+	}
+
+	if (de->dpll) {
+		dpll_device_unregister(de->dpll);
+		dpll_device_free(de->dpll);
+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
+	}
+
+	kthread_cancel_delayed_work_sync(&d->work);
+	if (d->kworker) {
+		kthread_destroy_worker(d->kworker);
+		d->kworker = NULL;
+		dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
+	}
+}
+
+/**
+ * ice_dpll_init - Initialize DPLLs support
+ * @pf: board private structure
+ *
+ * Set up the device as owner of DPLLs registering them and pins connected
+ * within Linux dpll subsystem. Allow userpsace to obtain state of DPLL
+ * and handling of DPLL configuration requests.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+int ice_dpll_init(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	int err;
+
+	mutex_init(&d->lock);
+	mutex_lock(&d->lock);
+	err = ice_dpll_init_info(pf);
+	if (err)
+		goto unlock;
+	err = ice_dpll_init_dpll(pf);
+	if (err)
+		goto release;
+	err = ice_dpll_register_pins(pf, d->eec.dpll, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (err)
+		goto release;
+	err = ice_dpll_register_pins(pf, d->eec.dpll, ICE_DPLL_PIN_TYPE_OUTPUT);
+	if (err)
+		goto release;
+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
+					    ICE_DPLL_PIN_TYPE_SOURCE);
+	if (err)
+		goto release;
+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
+					    ICE_DPLL_PIN_TYPE_OUTPUT);
+	if (err)
+		goto release;
+	set_bit(ICE_FLAG_DPLL, pf->flags);
+	err = ice_dpll_init_worker(pf);
+	if (err)
+		goto release;
+	mutex_unlock(&d->lock);
+	dev_dbg(ice_pf_to_dev(pf), "DPLLs init successful\n");
+
+	return err;
+release:
+	__ice_dpll_release(pf);
+unlock:
+	clear_bit(ICE_FLAG_DPLL, pf->flags);
+	mutex_unlock(&d->lock);
+	mutex_destroy(&d->lock);
+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure\n");
+
+	return err;
+}
+
+/**
+ * ice_dpll_release - Disable the driver/HW support for DPLLs and unregister
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * This function handles the cleanup work required from the initialization by
+ * freeing resources and unregistering the dpll.
+ */
+void ice_dpll_release(struct ice_pf *pf)
+{
+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
+		mutex_lock(&pf->dplls.lock);
+		clear_bit(ICE_FLAG_DPLL, pf->flags);
+		__ice_dpll_release(pf);
+		mutex_unlock(&pf->dplls.lock);
+		mutex_destroy(&pf->dplls.lock);
+	}
+}
+
+/**
+ * ice_dpll_rclk_pin_init - init the pin info for recovered clock
+ * @attr: structure with pin attributes
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+void ice_dpll_rclk_pin_init(struct ice_dpll_pin *p)
+{
+	p->flags = ICE_DPLL_RCLK_SOURCE_FLAG_EN;
+	p->type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+	set_bit(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ, &p->signal_type_mask);
+	set_bit(DPLL_PIN_MODE_CONNECTED,	  &p->mode_supported_mask);
+	set_bit(DPLL_PIN_MODE_DISCONNECTED,	  &p->mode_supported_mask);
+	set_bit(DPLL_PIN_MODE_SOURCE,		  &p->mode_supported_mask);
+	ice_dpll_pin_update(0, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * __ice_dpll_rclk_release - unregister the recovered pin for dpll device
+ * @pf: board private structure
+ *
+ * This function handles the cleanup work required from the initialization by
+ * freeing resources and unregistering the recovered pin.
+ */
+void __ice_dpll_rclk_release(struct ice_pf *pf)
+{
+	int ret = 0;
+
+	if (pf->dplls.eec.dpll) {
+		if (pf->dplls.rclk[0].pin)
+			ret = dpll_pin_deregister(pf->dplls.eec.dpll,
+						  pf->dplls.rclk[0].pin);
+		dpll_pin_free(pf->dplls.rclk->pin);
+		kfree(pf->dplls.rclk);
+	}
+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
+}
+
+/**
+ * ice_dpll_rclk_pins_init - init the pin for recovered clock
+ * @pf: board private structure
+ * @first_parent: pointer to a first parent pin
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+int ice_dpll_rclk_pins_init(struct ice_pf *pf, struct ice_dpll_pin *first_parent)
+{
+	struct ice_dpll_pin *parent, *p;
+	char *name;
+	int i, ret;
+
+	if (pf->dplls.rclk)
+		return -EEXIST;
+	pf->dplls.rclk = kcalloc(pf->dplls.num_rclk, sizeof(*pf->dplls.rclk),
+				 GFP_KERNEL);
+	if (!pf->dplls.rclk)
+		goto release;
+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
+		p = &pf->dplls.rclk[i];
+		if (!p)
+			goto release;
+		ice_dpll_rclk_pin_init(p);
+		parent = first_parent + i;
+		if (!parent)
+			goto release;
+		p->idx = i;
+		name = kcalloc(DPLL_PIN_DESC_LEN, sizeof(*p->name), GFP_KERNEL);
+		if (!name)
+			goto release;
+		snprintf(name, DPLL_PIN_DESC_LEN - 1, "%s-%u",
+			 parent->name, pf->hw.pf_id);
+		p->name = name;
+		p->pin = dpll_pin_alloc(p->name, p->type);
+		if (IS_ERR_OR_NULL(p->pin))
+			goto release;
+		ret = dpll_muxed_pin_register(pf->dplls.eec.dpll, parent->name,
+					      p->pin, &ice_dpll_rclk_ops, pf);
+		if (ret)
+			goto release;
+		ret = dpll_shared_pin_register(pf->dplls.eec.dpll,
+					       pf->dplls.pps.dpll,
+					       p->name,
+					       &ice_dpll_rclk_ops, pf);
+		if (ret)
+			goto release;
+	}
+
+	return ret;
+release:
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s releasing - p: %p, parent:%p, p->pin:%p name:%s, ret:%d\n",
+		__func__, p, parent, p->pin, name, ret);
+	__ice_dpll_rclk_release(pf);
+	return -ENOMEM;
+}
+
+/**
+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
+ * @pf: board private structure
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf)
+{
+	u64 clock_id = 0;
+
+	ice_generate_clock_id(pf, &clock_id);
+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,
+							 DPLL_TYPE_EEC, 0);
+	if (!pf->dplls.eec.dpll)
+		return -EFAULT;
+	pf->dplls.pps.dpll = dpll_device_get_by_clock_id(clock_id,
+							 DPLL_TYPE_PPS, 0);
+	if (!pf->dplls.pps.dpll)
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_parent_pins_init - initialize the recovered clock parent pins
+ * @pf: board private structure
+ * @base_rclk_idx: number of first recovered clock pin in DPLL
+ *
+ * This function shall be executed only if ICE_FLAG_DPLL feature is not
+ * supported.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+static int ice_dpll_rclk_parent_pins_init(struct ice_pf *pf, u8 base_rclk_idx)
+{
+	int i;
+
+	if (pf->dplls.inputs)
+		return -EINVAL;
+	pf->dplls.inputs = kcalloc(pf->dplls.num_rclk,
+				   sizeof(*pf->dplls.inputs), GFP_KERNEL);
+
+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
+		const char *desc;
+
+		desc = ice_cgu_get_pin_name(&pf->hw, base_rclk_idx + i, true);
+		if (!desc)
+			return -EINVAL;
+		pf->dplls.inputs[i].name = desc;
+	}
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_init - Enable support for DPLL's PHY clock recovery
+ * @pf: board private structure
+ *
+ * Context:
+ * Acquires a pf->dplls.lock. If PF is not an owner of DPLL it shall find and
+ * connect its pins with the device dpll.
+ *
+ * This function handles enablement of PHY clock recovery part for timesync
+ * capabilities.
+ * Prepares and initalizes resources required to register its PHY clock sources
+ * within DPLL subsystem.
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+int ice_dpll_rclk_init(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *first_parent = NULL;
+	u8 base_rclk_idx;
+	int ret;
+
+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &base_rclk_idx,
+					&pf->dplls.num_rclk);
+	if (ret)
+		return ret;
+
+	mutex_lock(&pf->dplls.lock);
+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
+		ret = ice_dpll_rclk_find_dplls(pf);
+		dev_dbg(ice_pf_to_dev(pf), "ecc:%p, pps:%p\n",
+			pf->dplls.eec.dpll, pf->dplls.pps.dpll);
+		if (ret)
+			goto unlock;
+		ret = ice_dpll_rclk_parent_pins_init(pf, base_rclk_idx);
+		if (ret)
+			goto unlock;
+		first_parent = &pf->dplls.inputs[0];
+	} else {
+		first_parent = &pf->dplls.inputs[base_rclk_idx];
+	}
+	if (!first_parent) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	ret = ice_dpll_rclk_pins_init(pf, first_parent);
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK init ret=%d\n", ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_release - Disable the support for DPLL's PHY clock recovery
+ * @pf: board private structure
+ *
+ * Context:
+ * Acquires a pf->dplls.lock. Requires dplls to be present, must be called
+ * before dplls are realesed.
+ *
+ * This function handles the cleanup work of resources allocated for enablement
+ * of PHY recovery clock mechanics.
+ * Unregisters RCLK pins and frees pin's memory allocated by ice_dpll_rclk_init.
+ */
+void ice_dpll_rclk_release(struct ice_pf *pf)
+{
+	int i, ret = 0;
+
+	if (!pf->dplls.rclk)
+		return;
+
+	mutex_lock(&pf->dplls.lock);
+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
+		if (pf->dplls.rclk[i].pin) {
+			dpll_pin_deregister(pf->dplls.eec.dpll,
+					    pf->dplls.rclk[i].pin);
+			dpll_pin_deregister(pf->dplls.pps.dpll,
+					    pf->dplls.rclk[i].pin);
+			dpll_pin_free(pf->dplls.rclk[i].pin);
+			pf->dplls.rclk[i].pin = NULL;
+		}
+		kfree(pf->dplls.rclk[i].name);
+		pf->dplls.rclk[i].name = NULL;
+	}
+	/* inputs were prepared only for RCLK, release them here */
+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
+		kfree(pf->dplls.inputs);
+		pf->dplls.inputs = NULL;
+	}
+	kfree(pf->dplls.rclk);
+	pf->dplls.rclk = NULL;
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
new file mode 100644
index 000000000000..3390d60f2fab
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2022, Intel Corporation. */
+
+#ifndef _ICE_DPLL_H_
+#define _ICE_DPLL_H_
+
+#include "ice.h"
+
+#define ICE_DPLL_PRIO_MAX	0xF
+
+/** ice_dpll_pin - store info about pins
+ * @pin: dpll pin structure
+ * @flags: pin flags returned from HW
+ * @idx: ice pin private idx
+ * @type: type of a pin
+ * @signal_type: current signal type
+ * @signal_type_mask: signal types supported
+ * @freq: current frequency of a pin
+ * @mode_mask: current pin modes as bitmask
+ * @mode_supported_mask: supported pin modes
+ * @name: pin name
+ */
+struct ice_dpll_pin {
+	struct dpll_pin *pin;
+#define ICE_DPLL_RCLK_SOURCE_FLAG_EN	BIT(0)
+	u8 flags;
+	u8 idx;
+	enum dpll_pin_type type;
+	enum dpll_pin_signal_type signal_type;
+	unsigned long signal_type_mask;
+	u32 freq;
+	unsigned long mode_mask;
+	unsigned long mode_supported_mask;
+	const char *name;
+};
+
+/** ice_dpll - store info required for DPLL control
+ * @dpll: pointer to dpll dev
+ * @dpll_idx: index of dpll on the NIC
+ * @source_idx: source currently selected
+ * @prev_source_idx: source previously selected
+ * @ref_state: state of dpll reference signals
+ * @eec_mode: eec_mode dpll is configured for
+ * @phase_offset: phase delay of a dpll
+ * @input_prio: priorities of each input
+ * @dpll_state: current dpll sync state
+ * @prev_dpll_state: last dpll sync state
+ */
+struct ice_dpll {
+	struct dpll_device *dpll;
+	int dpll_idx;
+	u8 source_idx;
+	u8 prev_source_idx;
+	u8 ref_state;
+	u8 eec_mode;
+	s64 phase_offset;
+	u8 *input_prio;
+	enum ice_cgu_state dpll_state;
+	enum ice_cgu_state prev_dpll_state;
+};
+
+/** ice_dplls - store info required for CCU (clock controlling unit)
+ * @kworker: periodic worker
+ * @work: periodic work
+ * @lock: locks access to configuration of a dpll
+ * @eec: pointer to EEC dpll dev
+ * @pps: pointer to PPS dpll dev
+ * @inputs: input pins pointer
+ * @outputs: output pins pointer
+ * @rclk: recovered pins pointer
+ * @num_inputs: number of input pins available on dpll
+ * @num_outputs: number of output pins available on dpll
+ * @num_rclk: number of recovered clock pins available on dpll
+ * @cgu_state_acq_err_num: number of errors returned during periodic work
+ */
+struct ice_dplls {
+	struct kthread_worker *kworker;
+	struct kthread_delayed_work work;
+	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 *rclk;
+	u32 num_inputs;
+	u32 num_outputs;
+	u8 num_rclk;
+	int cgu_state_acq_err_num;
+};
+
+int ice_dpll_init(struct ice_pf *pf);
+
+void ice_dpll_release(struct ice_pf *pf);
+
+int ice_dpll_rclk_init(struct ice_pf *pf);
+
+void ice_dpll_rclk_release(struct ice_pf *pf);
+
+#endif
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index a9a7f8b52140..8b65f4ad245e 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4896,6 +4896,12 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
 		ice_ptp_init(pf);
 
+	if (ice_is_feature_supported(pf, ICE_F_CGU))
+		ice_dpll_init(pf);
+
+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
+		ice_dpll_rclk_init(pf);
+
 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
 		ice_gnss_init(pf);
 
@@ -5078,6 +5084,10 @@ static void ice_remove(struct pci_dev *pdev)
 		ice_ptp_release(pf);
 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
 		ice_gnss_exit(pf);
+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
+		ice_dpll_rclk_release(pf);
+	if (ice_is_feature_supported(pf, ICE_F_CGU))
+		ice_dpll_release(pf);
 	if (!ice_is_safe_mode(pf))
 		ice_remove_arfs(pf);
 	ice_setup_mc_magic_wake(pf);
-- 
2.30.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
@ 2023-01-17 18:00   ` Vadim Fedorenko
  0 siblings, 0 replies; 44+ messages in thread
From: Vadim Fedorenko @ 2023-01-17 18:00 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, linux-arm-kernel, linux-clk, Milena Olech,
	Michal Michalik, Vadim Fedorenko

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

Control over clock generation unit is required for further development
of Synchronous Ethernet feature. Interface provides ability to obtain
current state of a dpll, its sources and outputs which are pins, and
allows their configuration.

Co-developed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Co-developed-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Michal Michalik <michal.michalik@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
---
 drivers/net/ethernet/intel/Kconfig        |    1 +
 drivers/net/ethernet/intel/ice/Makefile   |    3 +-
 drivers/net/ethernet/intel/ice/ice.h      |    4 +
 drivers/net/ethernet/intel/ice/ice_dpll.c | 2115 +++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_dpll.h |   99 +
 drivers/net/ethernet/intel/ice/ice_main.c |   10 +
 6 files changed, 2231 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
 create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h

diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 3facb55b7161..dcf0e12991bf 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -300,6 +300,7 @@ config ICE
 	select DIMLIB
 	select NET_DEVLINK
 	select PLDMFW
+	select DPLL
 	help
 	  This driver supports Intel(R) Ethernet Connection E800 Series of
 	  devices.  For more information on how to identify your adapter, go
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 9183d480b70b..ebfd456f76cd 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -32,7 +32,8 @@ ice-y := ice_main.o	\
 	 ice_lag.o	\
 	 ice_ethtool.o  \
 	 ice_repr.o	\
-	 ice_tc_lib.o
+	 ice_tc_lib.o	\
+	 ice_dpll.o
 ice-$(CONFIG_PCI_IOV) +=	\
 	ice_sriov.o		\
 	ice_virtchnl.o		\
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 3368dba42789..efc1844ef77c 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -73,6 +73,7 @@
 #include "ice_lag.h"
 #include "ice_vsi_vlan_ops.h"
 #include "ice_gnss.h"
+#include "ice_dpll.h"
 
 #define ICE_BAR0		0
 #define ICE_REQ_DESC_MULTIPLE	32
@@ -198,6 +199,7 @@
 enum ice_feature {
 	ICE_F_DSCP,
 	ICE_F_PTP_EXTTS,
+	ICE_F_PHY_RCLK,
 	ICE_F_SMA_CTRL,
 	ICE_F_CGU,
 	ICE_F_GNSS,
@@ -509,6 +511,7 @@ enum ice_pf_flags {
 	ICE_FLAG_PLUG_AUX_DEV,
 	ICE_FLAG_MTU_CHANGED,
 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
 	ICE_PF_FLAGS_NBITS		/* must be last */
 };
 
@@ -633,6 +636,7 @@ struct ice_pf {
 #define ICE_VF_AGG_NODE_ID_START	65
 #define ICE_MAX_VF_AGG_NODES		32
 	struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
+	struct ice_dplls dplls;
 };
 
 struct ice_netdev_priv {
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
new file mode 100644
index 000000000000..6ed1fcee4e03
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,2115 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022, Intel Corporation. */
+
+#include "ice.h"
+#include "ice_lib.h"
+#include "ice_trace.h"
+#include <linux/dpll.h>
+#include <uapi/linux/dpll.h>
+
+#define CGU_STATE_ACQ_ERR_THRESHOLD	50
+#define ICE_DPLL_FREQ_1_HZ		1
+#define ICE_DPLL_FREQ_10_MHZ		10000000
+
+/**
+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock status
+ */
+static const enum dpll_lock_status
+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
+};
+
+/**
+ * ice_dpll_pin_type - enumerate ince pin types
+ */
+enum ice_dpll_pin_type {
+	ICE_DPLL_PIN_INVALID = 0,
+	ICE_DPLL_PIN_TYPE_SOURCE,
+	ICE_DPLL_PIN_TYPE_OUTPUT,
+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
+};
+
+/**
+ * pin_type_name - string names of ice pin types
+ */
+static const char * const pin_type_name[] = {
+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
+};
+
+/**
+ * ice_dpll_pin_signal_type_to_freq - translate pin_signal_type to freq value
+ * @sig_type: signal type to find frequency
+ * @freq: on success - frequency of a signal type
+ *
+ * Return:
+ * * 0 - frequency valid
+ * * negative - error
+ */
+static inline int
+ice_dpll_pin_signal_type_to_freq(const enum dpll_pin_signal_type sig_type,
+				 u32 *freq)
+{
+	if (sig_type == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
+		return -EINVAL;
+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_1_PPS)
+		*freq = ICE_DPLL_FREQ_1_HZ;
+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_10_MHZ)
+		*freq = ICE_DPLL_FREQ_10_MHZ;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_pin_freq_to_signal_type - translate pin freq to signal type
+ * @freq: frequency to translate
+ *
+ * Return: signal type of a pin based on frequency.
+ */
+static inline enum dpll_pin_signal_type
+ice_dpll_pin_freq_to_signal_type(u32 freq)
+{
+	if (freq == ICE_DPLL_FREQ_1_HZ)
+		return DPLL_PIN_SIGNAL_TYPE_1_PPS;
+	else if (freq == ICE_DPLL_FREQ_10_MHZ)
+		return DPLL_PIN_SIGNAL_TYPE_10_MHZ;
+	else
+		return DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ;
+}
+
+/**
+ * ice_dpll_pin_signal_type_set - set pin's signal type in hardware
+ * @pf: Board private structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being configured
+ * @sig_type: signal type to be set
+ *
+ * Translate pin signal type to frequency and set it on a pin.
+ *
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+__ice_dpll_pin_signal_type_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
+			       const enum ice_dpll_pin_type pin_type,
+			       const enum dpll_pin_signal_type sig_type)
+{
+	u8 flags;
+	u32 freq;
+	int ret;
+
+	ret = ice_dpll_pin_signal_type_to_freq(sig_type, &freq);
+	if (ret)
+		return ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
+					       pin->flags, freq, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags = pin->flags | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
+						0, freq, 0);
+	} else {
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		dev_dbg(ice_pf_to_dev(pf),
+			"err:%d %s failed to set pin freq:%u on pin:%u\n",
+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
+			freq, pin->idx);
+	} else {
+		pin->signal_type = sig_type;
+		pin->freq = freq;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_enable - enable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being enabled
+ *
+ * Enable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		    const enum ice_dpll_pin_type pin_type)
+{
+	u8 flags = pin->flags;
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		flags |= ICE_DPLL_RCLK_SOURCE_FLAG_EN;
+		ret = 0;
+	}
+	if (ret)
+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+			"err:%d %s failed to enable %s pin:%u\n",
+			ret, ice_aq_str(hw->adminq.sq_last_status),
+			pin_type_name[pin_type], pin->idx);
+	else
+		pin->flags = flags;
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_disable - disable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being disabled
+ *
+ * Disable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		     enum ice_dpll_pin_type pin_type)
+{
+	u8 flags = pin->flags;
+	int ret;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		flags &= ~(ICE_DPLL_RCLK_SOURCE_FLAG_EN);
+		ret = ice_aq_set_phy_rec_clk_out(hw, pin->idx, false,
+						 &pin->freq);
+	}
+
+	if (ret)
+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+			"err:%d %s failed to disable %s pin:%u\n",
+			ret, ice_aq_str(hw->adminq.sq_last_status),
+			pin_type_name[pin_type], pin->idx);
+	else
+		pin->flags = flags;
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_update - update pin's mode
+ * @hw: private board struct
+ * @pin: structure with pin attributes to be updated
+ * @pin_type: type of pin being updated
+ *
+ * Determine pin current mode, frequency and signal type. Then update struct
+ * holding the pin info.
+ *
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+int
+ice_dpll_pin_update(struct ice_hw *hw, struct ice_dpll_pin *pin,
+		    const enum ice_dpll_pin_type pin_type)
+{
+	int ret;
+
+	pin->mode_mask = 0;
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		ret = ice_aq_get_input_pin_cfg(hw, pin->idx, NULL, NULL, NULL,
+					       &pin->flags, &pin->freq, NULL);
+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags)
+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
+		else
+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		ret = ice_aq_get_output_pin_cfg(hw, pin->idx, &pin->flags,
+						NULL, &pin->freq, NULL);
+		set_bit(DPLL_PIN_MODE_OUTPUT, &pin->mode_mask);
+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags)
+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
+		else
+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
+		if (ICE_DPLL_RCLK_SOURCE_FLAG_EN & pin->flags)
+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
+		else
+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
+		ret = 0;
+	}
+	pin->signal_type = ice_dpll_pin_freq_to_signal_type(pin->freq);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_mode_set - set pin's mode
+ * @pf: Board private structure
+ * @pin: pointer to a pin
+ * @pin_type: type of modified pin
+ * @mode: requested mode
+ *
+ * Determine requested pin mode set it on a pin.
+ *
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+ice_dpll_pin_mode_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
+		      const enum ice_dpll_pin_type pin_type,
+		      const enum dpll_pin_mode mode)
+{
+	int ret;
+
+	if (!test_bit(mode, &pin->mode_supported_mask))
+		return -EINVAL;
+
+	if (test_bit(mode, &pin->mode_mask))
+		return 0;
+
+	if (mode == DPLL_PIN_MODE_CONNECTED)
+		ret = ice_dpll_pin_enable(&pf->hw, pin, pin_type);
+	else if (mode == DPLL_PIN_MODE_DISCONNECTED)
+		ret = ice_dpll_pin_disable(&pf->hw, pin, pin_type);
+	else
+		ret = -EINVAL;
+
+	if (!ret)
+		ret = ice_dpll_pin_update(&pf->hw, pin, pin_type);
+
+	return ret;
+}
+
+/**
+ * ice_find_dpll - find ice_dpll on a pf
+ * @pf: private board structure
+ * @dpll: kernel's dpll_device pointer to be searched
+ *
+ * Return:
+ * * pointer if ice_dpll with given device dpll pointer is found
+ * * NULL if not found
+ */
+static struct ice_dpll
+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
+{
+	if (!pf || !dpll)
+		return NULL;
+
+	return (dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
+		dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL);
+}
+
+/**
+ * ice_find_pin - find ice_dpll_pin on a pf
+ * @pf: private board structure
+ * @pin: kernel's dpll_pin pointer to be searched for
+ * @pin_type: type of pins to be searched for
+ *
+ * Find and return internal ice pin info pointer holding data of given dpll subsystem
+ * pin pointer.
+ *
+ * Return:
+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer was
+ * found in pf internal pin data.
+ * * NULL - if pin was not found.
+ */
+static struct ice_dpll_pin
+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
+	      enum ice_dpll_pin_type pin_type)
+
+{
+	struct ice_dpll_pin *pins;
+	int pin_num, i;
+
+	if (!pin || !pf)
+		return NULL;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		pins = pf->dplls.inputs;
+		pin_num = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		pins = pf->dplls.outputs;
+		pin_num = pf->dplls.num_outputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
+		pins = pf->dplls.rclk;
+		pin_num = pf->dplls.num_rclk;
+	} else {
+		return NULL;
+	}
+
+	for (i = 0; i < pin_num; i++)
+		if (pin == pins[i].pin)
+			return &pins[i];
+
+	return NULL;
+}
+
+/**
+ * ice_dpll_hw_source_prio_set - set source priority value in hardware
+ * @pf: board private structure
+ * @dpll: ice dpll pointer
+ * @pin: ice pin pointer
+ * @prio: priority value being set on a dpll
+ *
+ * Internal wrapper for setting the priority in the hardware.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_hw_source_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
+			    struct ice_dpll_pin *pin, const u32 prio)
+{
+	int ret;
+
+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
+				      (u8)prio);
+	if (ret)
+		dev_dbg(ice_pf_to_dev(pf),
+			"err:%d %s failed to set pin prio:%u on pin:%u\n",
+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
+			prio, pin->idx);
+	else
+		dpll->input_prio[pin->idx] = prio;
+
+	return ret;
+}
+
+/**
+ * ice_dpll_lock_status_get - get dpll lock status callback
+ * @dpll: registered dpll pointer
+ * @status: on success holds dpll's lock status
+ *
+ * Dpll subsystem callback, provides dpll's lock status.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_lock_status_get(const struct dpll_device *dpll,
+				    enum dpll_lock_status *status)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		return -EFAULT;
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
+	mutex_lock(&pf->dplls.lock);
+	*status = ice_dpll_status[d->dpll_state];
+	mutex_unlock(&pf->dplls.lock);
+
+	return 0;
+}
+
+/**
+ * ice_dpll_source_idx_get - get dpll's source index
+ * @dpll: registered dpll pointer
+ * @pin_idx: on success holds currently selected source pin index
+ *
+ * Dpll subsystem callback. Provides index of a source dpll is trying to lock
+ * with.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_idx_get(const struct dpll_device *dpll, u32 *pin_idx)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+	int ret = 0;
+
+	mutex_lock(&pf->dplls.lock);
+	d = ice_find_dpll(pf, dpll);
+	if (!d) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	*pin_idx = (u32)d->source_idx;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p d:%p, idx:%u\n",
+		__func__, dpll, pf, d, *pin_idx);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_mode_get - get dpll's working mode
+ * @dpll: registered dpll pointer
+ * @mode: on success holds current working mode of dpll
+ *
+ * Dpll subsystem callback. Provides working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_get(const struct dpll_device *dpll,
+			     enum dpll_mode *mode)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+	int ret = 0;
+
+	mutex_lock(&pf->dplls.lock);
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		ret = -EFAULT;
+	else
+		*mode = DPLL_MODE_AUTOMATIC;
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_mode_get - check if dpll's working mode is supported
+ * @dpll: registered dpll pointer
+ * @mode: mode to be checked for support
+ *
+ * Dpll subsystem callback. Provides information if working mode is supported
+ * by dpll.
+ *
+ * Return:
+ * * true - mode is supported
+ * * false - mode is not supported
+ */
+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
+				    const enum dpll_mode mode)
+{
+	struct ice_pf *pf = dpll_priv(dpll);
+	struct ice_dpll *d;
+	bool ret = true;
+
+	mutex_lock(&pf->dplls.lock);
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		ret = false;
+	else
+		if (mode != DPLL_MODE_AUTOMATIC)
+			ret = false;
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_signal_type_supported - if pin signal type is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type being checked for support
+ * @pin_type: type of a pin being checked
+ *
+ * Check is signal type is supported on given pin/dpll pair.
+ *
+ * Return:
+ * * true - supported
+ * * false - not supported
+ */
+static bool
+ice_dpll_pin_signal_type_supported(const struct dpll_device *dpll,
+				   const struct dpll_pin *pin,
+				   const enum dpll_pin_signal_type sig_type,
+				   const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	bool supported = false;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (p) {
+		if (test_bit(sig_type, &p->signal_type_mask))
+			supported = true;
+	}
+	mutex_unlock(&pf->dplls.lock);
+
+	return supported;
+}
+
+/**
+ * ice_dpll_rclk_signal_type_supported - if rclk source signal type is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type being checked for support
+ *
+ * Dpll subsystem callback. Check is signal type is supported on given pin/dpll
+ * pair.
+ *
+ * Return:
+ * * true - supported
+ * * false - not supported
+ */
+static bool
+ice_dpll_rclk_signal_type_supported(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin,
+				    const enum dpll_pin_signal_type sig_type)
+{
+	const enum ice_dpll_pin_type t = ICE_DPLL_PIN_TYPE_RCLK_SOURCE;
+
+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type, t);
+}
+
+/**
+ * ice_dpll_source_signal_type_supported - if source signal type is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type being checked for support
+ *
+ * Dpll subsystem callback. Check is signal type is supported on given pin/dpll
+ * pair.
+ *
+ * Return:
+ * * true - supported
+ * * false - not supported
+ */
+static bool
+ice_dpll_source_signal_type_supported(const struct dpll_device *dpll,
+				      const struct dpll_pin *pin,
+				      const enum dpll_pin_signal_type sig_type)
+{
+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
+						  ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_output_signal_type_supported - if output pin signal type is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type being checked
+ *
+ * Dpll subsystem callback. Check if signal type is supported on given pin/dpll
+ * pair.
+ *
+ * Return:
+ * * true - supported
+ * * false - not supported
+ */
+static bool
+ice_dpll_output_signal_type_supported(const struct dpll_device *dpll,
+				      const struct dpll_pin *pin,
+				      const enum dpll_pin_signal_type sig_type)
+{
+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
+						  ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_signal_type_get - get dpll's pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: on success - current signal type used on the pin
+ * @pin_type: type of a pin being checked
+ *
+ * Find a pin and assign sig_type with its current signal type value.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_pin_signal_type_get(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					enum dpll_pin_signal_type *sig_type,
+					const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	int ret = -ENODEV;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (p) {
+		*sig_type = p->signal_type;
+		ret = 0;
+	}
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p\n",
+		__func__, dpll, pin, pf, p, p ? p->pin : NULL);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_output_signal_type_get - get dpll's output pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: on success - current signal type value used on the pin
+ *
+ * Dpll subsystem callback. Find current signal type of given pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_output_signal_type_get(const struct dpll_device *dpll,
+					   const struct dpll_pin *pin,
+					   enum dpll_pin_signal_type *sig_type)
+{
+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
+					    ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_signal_type_set - set dpll pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type to be set
+ * @pin_type: type of a pin being configured
+ *
+ * Handler for signal type modification on pins. Set signal type value for
+ * a given pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_pin_signal_type_set(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					const enum dpll_pin_signal_type sig_type,
+					const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	int ret = -EFAULT;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p)
+		goto unlock;
+	if (test_bit(sig_type, &p->signal_type_mask))
+		ret = __ice_dpll_pin_signal_type_set(pf, p, pin_type, sig_type);
+	else
+		ret = -EINVAL;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
+		__func__, dpll, pin, pf, p, p ? p->pin : NULL,  ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_output_signal_type_set - set dpll output pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type to be set
+ *
+ * Dpll subsystem callback. Wraps signal type modification handler.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_output_signal_type_set(const struct dpll_device *dpll,
+				const struct dpll_pin *pin,
+				const enum dpll_pin_signal_type sig_type)
+{
+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
+					    ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_mode_enable - enables a pin mode
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be set
+ * @pin_type: type of pin being modified
+ *
+ * Handler for enabling the pin mode.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_pin_mode_enable(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin,
+				    const enum dpll_pin_mode mode,
+				    const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	int ret = -EFAULT;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (p)
+		ret = ice_dpll_pin_mode_set(pf, p, pin_type, mode);
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
+		__func__, dpll, pin, pf, p, p ? p->pin : NULL, ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_mode_enable - enable rclk-source pin mode
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be set
+ *
+ * Dpll subsystem callback. Enables given mode on recovered clock source type
+ * pin.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_rclk_mode_enable(const struct dpll_device *dpll,
+				     const struct dpll_pin *pin,
+				     const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * ice_dpll_output_mode_enable - enable output pin mode
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be set
+ *
+ * Dpll subsystem callback. Enables given mode on output type pin.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_output_mode_enable(const struct dpll_device *dpll,
+				       const struct dpll_pin *pin,
+				       const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
+					 ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_mode_enable - enable source pin mode
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be set
+ *
+ * Dpll subsystem callback. Enables given mode on source type pin.
+ *
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int ice_dpll_source_mode_enable(const struct dpll_device *dpll,
+				       const struct dpll_pin *pin,
+				       const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
+					 ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_signal_type_get - get source pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @type: on success - source pin signal type
+ *
+ * Dpll subsystem callback. Get source pin signal type value.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_signal_type_get(const struct dpll_device *dpll,
+					   const struct dpll_pin *pin,
+					   enum dpll_pin_signal_type *sig_type)
+{
+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
+					    ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_signal_type_set - set dpll output pin signal type
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: signal type to be set
+ *
+ * dpll subsystem callback. Set source pin signal type value.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_source_signal_type_set(const struct dpll_device *dpll,
+				const struct dpll_pin *pin,
+				const enum dpll_pin_signal_type sig_type)
+{
+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
+					    ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_source_prio_get - get dpll's source prio
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @prio: on success - returns source priority on dpll
+ *
+ * Dpll subsystem callback. Handler for getting priority of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_prio_get(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin, u32 *prio)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll *d = NULL;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (*prio > ICE_DPLL_PRIO_MAX)
+		return ret;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (!p)
+		goto unlock;
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		goto unlock;
+	*prio = d->input_prio[p->idx];
+	ret = 0;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
+		__func__, dpll, pin, pf, ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_source_prio_set - set dpll source prio
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @prio: source priority to be set on dpll
+ *
+ * Dpll subsystem callback. Handler for setting priority of a source pin.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_source_prio_set(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin, const u32 prio)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll *d = NULL;
+	struct ice_dpll_pin *p;
+	int ret = -EINVAL;
+
+	if (prio > ICE_DPLL_PRIO_MAX)
+		return ret;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (!p)
+		goto unlock;
+	d = ice_find_dpll(pf, dpll);
+	if (!d)
+		goto unlock;
+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
+		__func__, dpll, pin, pf, ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_pin_mode_active -  check if given pin's mode is active
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ * @pin_type: type of a pin to be checked
+ *
+ * Handler for checking if given mode is active on a pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_pin_mode_active(const struct dpll_device *dpll,
+				     const struct dpll_pin *pin,
+				     const enum dpll_pin_mode mode,
+				     const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	bool ret = false;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+	if (!p)
+		goto unlock;
+	if (test_bit(mode, &p->mode_mask))
+		ret = true;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_mode_active - check if rclk source pin's mode is active
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
+ * on a recovered clock pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_rclk_mode_active(const struct dpll_device *dpll,
+				      const struct dpll_pin *pin,
+				      const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_active(dpll, pin, mode,
+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * ice_dpll_output_mode_active - check if output pin's mode is active
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
+ * on an output pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_output_mode_active(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_active(dpll, pin, mode,
+					ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_mode_active - check if source pin's mode is active
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
+ * on a source pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_source_mode_active(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_active(dpll, pin, mode,
+					ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_pin_mode_supported - check if pin's mode is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ * @pin_type: type of a pin being checked
+ *
+ * Handler for checking if given mode is supported on a pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_pin_mode_supported(const struct dpll_device *dpll,
+					const struct dpll_pin *pin,
+					const enum dpll_pin_mode mode,
+					const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	bool ret = false;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, pin_type);
+
+	if (!p)
+		goto unlock;
+	if (test_bit(mode, &p->mode_supported_mask))
+		ret = true;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_mode_supported - check if rclk pin's mode is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is
+ * supported on a clock recovery pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_rclk_mode_supported(const struct dpll_device *dpll,
+					 const struct dpll_pin *pin,
+					 const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
+					    ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * ice_dpll_output_mode_supported - check if output pin's mode is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is
+ * supported on an output pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_output_mode_supported(const struct dpll_device *dpll,
+					   const struct dpll_pin *pin,
+					   const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
+					    ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_source_mode_supported - check if source pin's mode is supported
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @mode: mode to be checked
+ *
+ * DPLL subsystem callback, Wraps handler for checking if given mode is
+ * supported on a source pin.
+ *
+ * Return:
+ * * true - mode is active
+ * * false - mode is not active
+ */
+static bool ice_dpll_source_mode_supported(const struct dpll_device *dpll,
+					   const struct dpll_pin *pin,
+					   const enum dpll_pin_mode mode)
+{
+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
+					    ICE_DPLL_PIN_TYPE_SOURCE);
+}
+
+/**
+ * ice_dpll_rclk_pin_sig_type_get - get signal type of rclk pin
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @sig_type: on success - holds a signal type of a pin
+ *
+ * DPLL subsystem callback, provides signal type of clock recovery pin.
+ *
+ * Return:
+ * * 0 - success, sig_type value is valid
+ * * negative - error
+ */
+static int ice_dpll_rclk_pin_sig_type_get(const struct dpll_device *dpll,
+					  const struct dpll_pin *pin,
+					  enum dpll_pin_signal_type *sig_type)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	int ret = 0;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (!p) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	*sig_type = p->signal_type;
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p sig_type:%d ret:%d\n",
+		__func__, dpll, pin, pf, *sig_type, ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_pin_net_if_index_get - get OS interface index callback
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @net_if_idx: on success - holds OS interface index
+ *
+ * dpll subsystem callback, obtains OS interface index and pass to the caller.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_rclk_pin_net_if_index_get(const struct dpll_device *dpll,
+					      const struct dpll_pin *pin,
+					      int *net_if_idx)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+
+	if (!pf->vsi[0] || pf->vsi[0]->netdev)
+		return -EAGAIN;
+	*net_if_idx = pf->vsi[0]->netdev->ifindex;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_pin_select - select a recovered clock pin as a valid source
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ *
+ * dpll subsystem callback, selects a pin for clock recovery,
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_rclk_pin_select(const struct dpll_device *dpll,
+				    const struct dpll_pin *pin)
+{
+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
+	struct ice_dpll_pin *p;
+	u32 freq;
+	int ret;
+
+	mutex_lock(&pf->dplls.lock);
+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+	if (!p) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	if (!(p->flags & ICE_DPLL_RCLK_SOURCE_FLAG_EN)) {
+		ret = -EPERM;
+		goto unlock;
+	}
+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, p->idx, true, &freq);
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
+		__func__, dpll, pin, pf, ret);
+
+	return ret;
+}
+
+static struct dpll_pin_ops ice_dpll_rclk_ops = {
+	.mode_enable = ice_dpll_rclk_mode_enable,
+	.mode_active = ice_dpll_rclk_mode_active,
+	.mode_supported = ice_dpll_rclk_mode_supported,
+	.signal_type_get = ice_dpll_rclk_pin_sig_type_get,
+	.signal_type_supported = ice_dpll_rclk_signal_type_supported,
+	.net_if_idx_get = ice_dpll_rclk_pin_net_if_index_get,
+	.select = ice_dpll_rclk_pin_select,
+};
+
+static struct dpll_pin_ops ice_dpll_source_ops = {
+	.signal_type_get = ice_dpll_source_signal_type_get,
+	.signal_type_set = ice_dpll_source_signal_type_set,
+	.signal_type_supported = ice_dpll_source_signal_type_supported,
+	.mode_active = ice_dpll_source_mode_active,
+	.mode_enable = ice_dpll_source_mode_enable,
+	.mode_supported = ice_dpll_source_mode_supported,
+	.prio_get = ice_dpll_source_prio_get,
+	.prio_set = ice_dpll_source_prio_set,
+};
+
+static struct dpll_pin_ops ice_dpll_output_ops = {
+	.signal_type_get = ice_dpll_output_signal_type_get,
+	.signal_type_set = ice_dpll_output_signal_type_set,
+	.signal_type_supported = ice_dpll_output_signal_type_supported,
+	.mode_active = ice_dpll_output_mode_active,
+	.mode_enable = ice_dpll_output_mode_enable,
+	.mode_supported = ice_dpll_output_mode_supported,
+};
+
+static struct dpll_device_ops ice_dpll_ops = {
+	.lock_status_get = ice_dpll_lock_status_get,
+	.source_pin_idx_get = ice_dpll_source_idx_get,
+	.mode_get = ice_dpll_mode_get,
+	.mode_supported = ice_dpll_mode_supported,
+};
+
+/**
+ * ice_dpll_release_info - release memory allocated for pins
+ * @pf: board private structure
+ *
+ * Release memory allocated for pins by ice_dpll_init_info function.
+ */
+static void ice_dpll_release_info(struct ice_pf *pf)
+{
+	kfree(pf->dplls.inputs);
+	pf->dplls.inputs = NULL;
+	kfree(pf->dplls.outputs);
+	pf->dplls.outputs = NULL;
+	kfree(pf->dplls.eec.input_prio);
+	pf->dplls.eec.input_prio = NULL;
+	kfree(pf->dplls.pps.input_prio);
+	pf->dplls.pps.input_prio = NULL;
+}
+
+/**
+ * ice_dpll_init_pins - initializes source or output pins information
+ * @pf: Board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Init information about input or output pins, cache them in pins struct.
+ */
+static int ice_dpll_init_pins(struct ice_pf *pf,
+			      const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
+	int ret = -EINVAL, num_pins, i;
+	struct ice_hw *hw = &pf->hw;
+	struct ice_dpll_pin *pins;
+	bool input;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		input = true;
+		pins = pf->dplls.inputs;
+		num_pins = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		input = false;
+		pins = pf->dplls.outputs;
+		num_pins = pf->dplls.num_outputs;
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_pins; i++) {
+		pins[i].idx = i;
+		pins[i].name = ice_cgu_get_pin_name(hw, i, input);
+		pins[i].type = ice_cgu_get_pin_type(hw, i, input);
+		set_bit(DPLL_PIN_MODE_CONNECTED,
+			&pins[i].mode_supported_mask);
+		set_bit(DPLL_PIN_MODE_DISCONNECTED,
+			&pins[i].mode_supported_mask);
+		if (input) {
+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
+						      &de->input_prio[i]);
+			if (ret)
+				return ret;
+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
+						      &dp->input_prio[i]);
+			if (ret)
+				return ret;
+			set_bit(DPLL_PIN_MODE_SOURCE,
+				&pins[i].mode_supported_mask);
+		} else {
+			set_bit(DPLL_PIN_MODE_OUTPUT,
+				&pins[i].mode_supported_mask);
+		}
+		pins[i].signal_type_mask =
+				ice_cgu_get_pin_sig_type_mask(hw, i, input);
+		ret = ice_dpll_pin_update(hw, &pins[i], pin_type);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
+ * @dpll_eec: dpll_eec dpll pointer
+ * @dpll_pps: dpll_pps dpll pointer
+ * @pins: pointer to pins array
+ * @count: number of pins
+ *
+ * Deregister and free pins of a given array of pins from dpll devices registered
+ * in dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * positive - number of errors encounterd on pin's deregistration.
+ */
+static int
+ice_dpll_release_pins(struct dpll_device *dpll_eec,
+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
+		      int count)
+{
+	int i, ret, err;
+
+	for (i = 0; i < count; i++) {
+		struct ice_dpll_pin *p = &pins[i];
+
+		if (p && p->pin) {
+			if (dpll_eec) {
+				ret = dpll_pin_deregister(dpll_eec, p->pin);
+				if (ret)
+					err++;
+			}
+			if (dpll_pps) {
+				ret = dpll_pin_deregister(dpll_pps, p->pin);
+				if (ret)
+					err++;
+			}
+			dpll_pin_free(p->pin);
+			p->pin = NULL;
+		}
+	}
+
+	return err;
+}
+
+/**
+ * ice_dpll_register_pins - register pins with a dpll
+ * @pf: board private structure
+ * @dpll: registered dpll pointer
+ * @pin_type: type of pins being registered
+ *
+ * Register source or output pins within given DPLL in a Linux dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_register_pins(struct ice_pf *pf, struct dpll_device *dpll,
+		       const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *pins;
+	struct dpll_pin_ops *ops;
+	int ret, i, count;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		ops = &ice_dpll_source_ops;
+		pins = pf->dplls.inputs;
+		count = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		ops = &ice_dpll_output_ops;
+		pins = pf->dplls.outputs;
+		count = pf->dplls.num_outputs;
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < count; i++) {
+		pins[i].pin = dpll_pin_alloc(pins[i].name, pins[i].type);
+		if (IS_ERR_OR_NULL(pins[i].pin))
+			return -ENOMEM;
+
+		ret = dpll_pin_register(dpll, pins[i].pin, ops, pf);
+		if (ret)
+			return -ENOSPC;
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_register_shared_pins - register shared pins in DPLL subsystem
+ * @pf: board private structure
+ * @dpll_o: registered dpll pointer (owner)
+ * @dpll: registered dpll pointer
+ * @type: type of pins being registered
+ *
+ * Register pins from given owner dpll within given dpll in Linux dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - error
+ */
+static int
+ice_dpll_register_shared_pins(struct ice_pf *pf, struct dpll_device *dpll_o,
+			      struct dpll_device *dpll,
+			      const enum ice_dpll_pin_type pin_type)
+{
+	struct ice_dpll_pin *pins;
+	struct dpll_pin_ops *ops;
+	int ret, i, count;
+
+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
+		ops = &ice_dpll_source_ops;
+		pins = pf->dplls.inputs;
+		count = pf->dplls.num_inputs;
+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
+		ops = &ice_dpll_output_ops;
+		pins = pf->dplls.outputs;
+		count = pf->dplls.num_outputs;
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < count; i++) {
+		ret = dpll_shared_pin_register(dpll_o, dpll, pins[i].name,
+					       ops, pf);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * ice_dpll_init_info - prepare pf's dpll information structure
+ * @pf: board private structure
+ *
+ * Acquire (from HW) and set basic dpll information (on pf->dplls struct).
+ *
+ * Return:
+ *  0 - success
+ *  negative - error
+ */
+static int ice_dpll_init_info(struct ice_pf *pf)
+{
+	struct ice_aqc_get_cgu_abilities abilities;
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_hw *hw = &pf->hw;
+	int ret, alloc_size;
+
+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
+	if (ret) {
+		dev_err(ice_pf_to_dev(pf),
+			"err:%d %s failed to read cgu abilities\n",
+			ret, ice_aq_str(hw->adminq.sq_last_status));
+		return ret;
+	}
+
+	de->dpll_idx = abilities.eec_dpll_idx;
+	dp->dpll_idx = abilities.pps_dpll_idx;
+	d->num_inputs = abilities.num_inputs;
+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
+	if (!d->inputs)
+		return -ENOMEM;
+
+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+	if (!de->input_prio)
+		return -ENOMEM;
+
+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+	if (!dp->input_prio)
+		return -ENOMEM;
+
+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (ret)
+		goto release_info;
+
+	d->num_outputs = abilities.num_outputs;
+	alloc_size = sizeof(*d->outputs) * d->num_outputs;
+	d->outputs = kzalloc(alloc_size, GFP_KERNEL);
+	if (!d->outputs)
+		goto release_info;
+
+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
+	if (ret)
+		goto release_info;
+
+	dev_dbg(ice_pf_to_dev(pf), "%s - success, inputs:%u, outputs:%u\n", __func__,
+		abilities.num_inputs, abilities.num_outputs);
+
+	return 0;
+
+release_info:
+	dev_err(ice_pf_to_dev(pf),
+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
+		__func__, d->inputs, de->input_prio,
+		dp->input_prio, d->outputs);
+	ice_dpll_release_info(pf);
+	return ret;
+}
+
+/**
+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
+ * @pf: board private structure
+ * @clock_id: holds generated clock_id
+ *
+ * Generates unique (per board) clock_id for allocation and search of dpll
+ * devices in Linux dpll subsystem.
+ */
+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
+{
+	*clock_id = pci_get_dsn(pf->pdev);
+}
+
+/**
+ * ice_dpll_init_dpll
+ * @pf: board private structure
+ *
+ * Allocate and register dpll in dpll subsystem.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - allocation fails
+ */
+static int ice_dpll_init_dpll(struct ice_pf *pf)
+{
+	struct device *dev = ice_pf_to_dev(pf);
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	u64 clock_id = 0;
+	int ret = 0;
+
+	ice_generate_clock_id(pf, &clock_id);
+
+	de->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_EEC,
+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
+	if (!de->dpll) {
+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (eec)\n");
+		return -ENOMEM;
+	}
+
+	dp->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_PPS,
+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
+	if (!dp->dpll) {
+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (pps)\n");
+		return -ENOMEM;
+	}
+
+	return ret;
+}
+
+/**
+ * ice_dpll_update_state
+ * @hw: board private structure
+ * @d: pointer to queried dpll device
+ *
+ * Poll current state of dpll from hw and update ice_dpll struct.
+ * Return:
+ * * 0 - success
+ * * negative - AQ failure
+ */
+static int ice_dpll_update_state(struct ice_hw *hw, struct ice_dpll *d)
+{
+	int ret;
+
+	ret = ice_get_cgu_state(hw, d->dpll_idx, d->prev_dpll_state,
+				&d->source_idx, &d->ref_state, &d->eec_mode,
+				&d->phase_offset, &d->dpll_state);
+
+	dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+		"update dpll=%d, src_idx:%u, state:%d, prev:%d\n",
+		d->dpll_idx, d->source_idx,
+		d->dpll_state, d->prev_dpll_state);
+
+	if (ret)
+		dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+			"update dpll=%d state failed, ret=%d %s\n",
+			d->dpll_idx, ret,
+			ice_aq_str(hw->adminq.sq_last_status));
+
+	return ret;
+}
+
+/**
+ * ice_dpll_notify_changes - notify dpll subsystem about changes
+ * @d: pointer do dpll
+ *
+ * Once change detected appropriate event is submitted to the dpll subsystem.
+ */
+static void ice_dpll_notify_changes(struct ice_dpll *d)
+{
+	if (d->prev_dpll_state != d->dpll_state) {
+		d->prev_dpll_state = d->dpll_state;
+		dpll_device_notify(d->dpll, DPLL_CHANGE_LOCK_STATUS);
+	}
+	if (d->prev_source_idx != d->source_idx) {
+		d->prev_source_idx = d->source_idx;
+		dpll_device_notify(d->dpll, DPLL_CHANGE_SOURCE_PIN);
+	}
+}
+
+/**
+ * ice_dpll_periodic_work - DPLLs periodic worker
+ * @work: pointer to kthread_work structure
+ *
+ * DPLLs periodic worker is responsible for polling state of dpll.
+ */
+static void ice_dpll_periodic_work(struct kthread_work *work)
+{
+	struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
+	struct ice_dpll *de = &pf->dplls.eec;
+	struct ice_dpll *dp = &pf->dplls.pps;
+	int ret = 0;
+
+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
+		return;
+	mutex_lock(&d->lock);
+	ret = ice_dpll_update_state(&pf->hw, de);
+	if (!ret)
+		ret = ice_dpll_update_state(&pf->hw, dp);
+	if (ret) {
+		d->cgu_state_acq_err_num++;
+		/* stop rescheduling this worker */
+		if (d->cgu_state_acq_err_num >
+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
+			dev_err(ice_pf_to_dev(pf),
+				"EEC/PPS DPLLs periodic work disabled\n");
+			return;
+		}
+	}
+	mutex_unlock(&d->lock);
+	ice_dpll_notify_changes(de);
+	ice_dpll_notify_changes(dp);
+
+	/* Run twice a second or reschedule if update failed */
+	kthread_queue_delayed_work(d->kworker, &d->work,
+				   ret ? msecs_to_jiffies(10) :
+				   msecs_to_jiffies(500));
+}
+
+/**
+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
+ * @pf: board private structure
+ *
+ * Create and start DPLLs periodic worker.
+ * Return:
+ * * 0 - success
+ * * negative - create worker failure
+ */
+static int ice_dpll_init_worker(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	struct kthread_worker *kworker;
+
+	ice_dpll_update_state(&pf->hw, &d->eec);
+	ice_dpll_update_state(&pf->hw, &d->pps);
+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
+	kworker = kthread_create_worker(0, "ice-dplls-%s",
+					dev_name(ice_pf_to_dev(pf)));
+	if (IS_ERR(kworker))
+		return PTR_ERR(kworker);
+	d->kworker = kworker;
+	d->cgu_state_acq_err_num = 0;
+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
+
+	return 0;
+}
+
+/**
+ * __ice_dpll_release - Disable the driver/HW support for DPLL and unregister
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * This function handles the cleanup work required from the initialization by
+ * freeing resources and unregistering the dpll.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void __ice_dpll_release(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	struct ice_dpll *de = &d->eec;
+	struct ice_dpll *dp = &d->pps;
+	int ret;
+
+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
+				    d->num_inputs);
+	if (ret)
+		dev_warn(ice_pf_to_dev(pf),
+			 "pin deregister on PPS dpll err=%d\n", ret);
+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
+				    d->num_outputs);
+	if (ret)
+		dev_warn(ice_pf_to_dev(pf),
+			 "pin deregister on PPS dpll err=%d\n", ret);
+	ice_dpll_release_info(pf);
+	if (dp->dpll) {
+		dpll_device_unregister(dp->dpll);
+		dpll_device_free(dp->dpll);
+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
+	}
+
+	if (de->dpll) {
+		dpll_device_unregister(de->dpll);
+		dpll_device_free(de->dpll);
+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
+	}
+
+	kthread_cancel_delayed_work_sync(&d->work);
+	if (d->kworker) {
+		kthread_destroy_worker(d->kworker);
+		d->kworker = NULL;
+		dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
+	}
+}
+
+/**
+ * ice_dpll_init - Initialize DPLLs support
+ * @pf: board private structure
+ *
+ * Set up the device as owner of DPLLs registering them and pins connected
+ * within Linux dpll subsystem. Allow userpsace to obtain state of DPLL
+ * and handling of DPLL configuration requests.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+int ice_dpll_init(struct ice_pf *pf)
+{
+	struct ice_dplls *d = &pf->dplls;
+	int err;
+
+	mutex_init(&d->lock);
+	mutex_lock(&d->lock);
+	err = ice_dpll_init_info(pf);
+	if (err)
+		goto unlock;
+	err = ice_dpll_init_dpll(pf);
+	if (err)
+		goto release;
+	err = ice_dpll_register_pins(pf, d->eec.dpll, ICE_DPLL_PIN_TYPE_SOURCE);
+	if (err)
+		goto release;
+	err = ice_dpll_register_pins(pf, d->eec.dpll, ICE_DPLL_PIN_TYPE_OUTPUT);
+	if (err)
+		goto release;
+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
+					    ICE_DPLL_PIN_TYPE_SOURCE);
+	if (err)
+		goto release;
+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
+					    ICE_DPLL_PIN_TYPE_OUTPUT);
+	if (err)
+		goto release;
+	set_bit(ICE_FLAG_DPLL, pf->flags);
+	err = ice_dpll_init_worker(pf);
+	if (err)
+		goto release;
+	mutex_unlock(&d->lock);
+	dev_dbg(ice_pf_to_dev(pf), "DPLLs init successful\n");
+
+	return err;
+release:
+	__ice_dpll_release(pf);
+unlock:
+	clear_bit(ICE_FLAG_DPLL, pf->flags);
+	mutex_unlock(&d->lock);
+	mutex_destroy(&d->lock);
+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure\n");
+
+	return err;
+}
+
+/**
+ * ice_dpll_release - Disable the driver/HW support for DPLLs and unregister
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * This function handles the cleanup work required from the initialization by
+ * freeing resources and unregistering the dpll.
+ */
+void ice_dpll_release(struct ice_pf *pf)
+{
+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
+		mutex_lock(&pf->dplls.lock);
+		clear_bit(ICE_FLAG_DPLL, pf->flags);
+		__ice_dpll_release(pf);
+		mutex_unlock(&pf->dplls.lock);
+		mutex_destroy(&pf->dplls.lock);
+	}
+}
+
+/**
+ * ice_dpll_rclk_pin_init - init the pin info for recovered clock
+ * @attr: structure with pin attributes
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+void ice_dpll_rclk_pin_init(struct ice_dpll_pin *p)
+{
+	p->flags = ICE_DPLL_RCLK_SOURCE_FLAG_EN;
+	p->type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+	set_bit(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ, &p->signal_type_mask);
+	set_bit(DPLL_PIN_MODE_CONNECTED,	  &p->mode_supported_mask);
+	set_bit(DPLL_PIN_MODE_DISCONNECTED,	  &p->mode_supported_mask);
+	set_bit(DPLL_PIN_MODE_SOURCE,		  &p->mode_supported_mask);
+	ice_dpll_pin_update(0, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
+}
+
+/**
+ * __ice_dpll_rclk_release - unregister the recovered pin for dpll device
+ * @pf: board private structure
+ *
+ * This function handles the cleanup work required from the initialization by
+ * freeing resources and unregistering the recovered pin.
+ */
+void __ice_dpll_rclk_release(struct ice_pf *pf)
+{
+	int ret = 0;
+
+	if (pf->dplls.eec.dpll) {
+		if (pf->dplls.rclk[0].pin)
+			ret = dpll_pin_deregister(pf->dplls.eec.dpll,
+						  pf->dplls.rclk[0].pin);
+		dpll_pin_free(pf->dplls.rclk->pin);
+		kfree(pf->dplls.rclk);
+	}
+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
+}
+
+/**
+ * ice_dpll_rclk_pins_init - init the pin for recovered clock
+ * @pf: board private structure
+ * @first_parent: pointer to a first parent pin
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+int ice_dpll_rclk_pins_init(struct ice_pf *pf, struct ice_dpll_pin *first_parent)
+{
+	struct ice_dpll_pin *parent, *p;
+	char *name;
+	int i, ret;
+
+	if (pf->dplls.rclk)
+		return -EEXIST;
+	pf->dplls.rclk = kcalloc(pf->dplls.num_rclk, sizeof(*pf->dplls.rclk),
+				 GFP_KERNEL);
+	if (!pf->dplls.rclk)
+		goto release;
+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
+		p = &pf->dplls.rclk[i];
+		if (!p)
+			goto release;
+		ice_dpll_rclk_pin_init(p);
+		parent = first_parent + i;
+		if (!parent)
+			goto release;
+		p->idx = i;
+		name = kcalloc(DPLL_PIN_DESC_LEN, sizeof(*p->name), GFP_KERNEL);
+		if (!name)
+			goto release;
+		snprintf(name, DPLL_PIN_DESC_LEN - 1, "%s-%u",
+			 parent->name, pf->hw.pf_id);
+		p->name = name;
+		p->pin = dpll_pin_alloc(p->name, p->type);
+		if (IS_ERR_OR_NULL(p->pin))
+			goto release;
+		ret = dpll_muxed_pin_register(pf->dplls.eec.dpll, parent->name,
+					      p->pin, &ice_dpll_rclk_ops, pf);
+		if (ret)
+			goto release;
+		ret = dpll_shared_pin_register(pf->dplls.eec.dpll,
+					       pf->dplls.pps.dpll,
+					       p->name,
+					       &ice_dpll_rclk_ops, pf);
+		if (ret)
+			goto release;
+	}
+
+	return ret;
+release:
+	dev_dbg(ice_pf_to_dev(pf),
+		"%s releasing - p: %p, parent:%p, p->pin:%p name:%s, ret:%d\n",
+		__func__, p, parent, p->pin, name, ret);
+	__ice_dpll_rclk_release(pf);
+	return -ENOMEM;
+}
+
+/**
+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
+ * @pf: board private structure
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf)
+{
+	u64 clock_id = 0;
+
+	ice_generate_clock_id(pf, &clock_id);
+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,
+							 DPLL_TYPE_EEC, 0);
+	if (!pf->dplls.eec.dpll)
+		return -EFAULT;
+	pf->dplls.pps.dpll = dpll_device_get_by_clock_id(clock_id,
+							 DPLL_TYPE_PPS, 0);
+	if (!pf->dplls.pps.dpll)
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_parent_pins_init - initialize the recovered clock parent pins
+ * @pf: board private structure
+ * @base_rclk_idx: number of first recovered clock pin in DPLL
+ *
+ * This function shall be executed only if ICE_FLAG_DPLL feature is not
+ * supported.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+static int ice_dpll_rclk_parent_pins_init(struct ice_pf *pf, u8 base_rclk_idx)
+{
+	int i;
+
+	if (pf->dplls.inputs)
+		return -EINVAL;
+	pf->dplls.inputs = kcalloc(pf->dplls.num_rclk,
+				   sizeof(*pf->dplls.inputs), GFP_KERNEL);
+
+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
+		const char *desc;
+
+		desc = ice_cgu_get_pin_name(&pf->hw, base_rclk_idx + i, true);
+		if (!desc)
+			return -EINVAL;
+		pf->dplls.inputs[i].name = desc;
+	}
+	return 0;
+}
+
+/**
+ * ice_dpll_rclk_init - Enable support for DPLL's PHY clock recovery
+ * @pf: board private structure
+ *
+ * Context:
+ * Acquires a pf->dplls.lock. If PF is not an owner of DPLL it shall find and
+ * connect its pins with the device dpll.
+ *
+ * This function handles enablement of PHY clock recovery part for timesync
+ * capabilities.
+ * Prepares and initalizes resources required to register its PHY clock sources
+ * within DPLL subsystem.
+ * Return:
+ * * 0 - success
+ * * negative - init failure
+ */
+int ice_dpll_rclk_init(struct ice_pf *pf)
+{
+	struct ice_dpll_pin *first_parent = NULL;
+	u8 base_rclk_idx;
+	int ret;
+
+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &base_rclk_idx,
+					&pf->dplls.num_rclk);
+	if (ret)
+		return ret;
+
+	mutex_lock(&pf->dplls.lock);
+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
+		ret = ice_dpll_rclk_find_dplls(pf);
+		dev_dbg(ice_pf_to_dev(pf), "ecc:%p, pps:%p\n",
+			pf->dplls.eec.dpll, pf->dplls.pps.dpll);
+		if (ret)
+			goto unlock;
+		ret = ice_dpll_rclk_parent_pins_init(pf, base_rclk_idx);
+		if (ret)
+			goto unlock;
+		first_parent = &pf->dplls.inputs[0];
+	} else {
+		first_parent = &pf->dplls.inputs[base_rclk_idx];
+	}
+	if (!first_parent) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+	ret = ice_dpll_rclk_pins_init(pf, first_parent);
+unlock:
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK init ret=%d\n", ret);
+
+	return ret;
+}
+
+/**
+ * ice_dpll_rclk_release - Disable the support for DPLL's PHY clock recovery
+ * @pf: board private structure
+ *
+ * Context:
+ * Acquires a pf->dplls.lock. Requires dplls to be present, must be called
+ * before dplls are realesed.
+ *
+ * This function handles the cleanup work of resources allocated for enablement
+ * of PHY recovery clock mechanics.
+ * Unregisters RCLK pins and frees pin's memory allocated by ice_dpll_rclk_init.
+ */
+void ice_dpll_rclk_release(struct ice_pf *pf)
+{
+	int i, ret = 0;
+
+	if (!pf->dplls.rclk)
+		return;
+
+	mutex_lock(&pf->dplls.lock);
+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
+		if (pf->dplls.rclk[i].pin) {
+			dpll_pin_deregister(pf->dplls.eec.dpll,
+					    pf->dplls.rclk[i].pin);
+			dpll_pin_deregister(pf->dplls.pps.dpll,
+					    pf->dplls.rclk[i].pin);
+			dpll_pin_free(pf->dplls.rclk[i].pin);
+			pf->dplls.rclk[i].pin = NULL;
+		}
+		kfree(pf->dplls.rclk[i].name);
+		pf->dplls.rclk[i].name = NULL;
+	}
+	/* inputs were prepared only for RCLK, release them here */
+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
+		kfree(pf->dplls.inputs);
+		pf->dplls.inputs = NULL;
+	}
+	kfree(pf->dplls.rclk);
+	pf->dplls.rclk = NULL;
+	mutex_unlock(&pf->dplls.lock);
+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
new file mode 100644
index 000000000000..3390d60f2fab
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2022, Intel Corporation. */
+
+#ifndef _ICE_DPLL_H_
+#define _ICE_DPLL_H_
+
+#include "ice.h"
+
+#define ICE_DPLL_PRIO_MAX	0xF
+
+/** ice_dpll_pin - store info about pins
+ * @pin: dpll pin structure
+ * @flags: pin flags returned from HW
+ * @idx: ice pin private idx
+ * @type: type of a pin
+ * @signal_type: current signal type
+ * @signal_type_mask: signal types supported
+ * @freq: current frequency of a pin
+ * @mode_mask: current pin modes as bitmask
+ * @mode_supported_mask: supported pin modes
+ * @name: pin name
+ */
+struct ice_dpll_pin {
+	struct dpll_pin *pin;
+#define ICE_DPLL_RCLK_SOURCE_FLAG_EN	BIT(0)
+	u8 flags;
+	u8 idx;
+	enum dpll_pin_type type;
+	enum dpll_pin_signal_type signal_type;
+	unsigned long signal_type_mask;
+	u32 freq;
+	unsigned long mode_mask;
+	unsigned long mode_supported_mask;
+	const char *name;
+};
+
+/** ice_dpll - store info required for DPLL control
+ * @dpll: pointer to dpll dev
+ * @dpll_idx: index of dpll on the NIC
+ * @source_idx: source currently selected
+ * @prev_source_idx: source previously selected
+ * @ref_state: state of dpll reference signals
+ * @eec_mode: eec_mode dpll is configured for
+ * @phase_offset: phase delay of a dpll
+ * @input_prio: priorities of each input
+ * @dpll_state: current dpll sync state
+ * @prev_dpll_state: last dpll sync state
+ */
+struct ice_dpll {
+	struct dpll_device *dpll;
+	int dpll_idx;
+	u8 source_idx;
+	u8 prev_source_idx;
+	u8 ref_state;
+	u8 eec_mode;
+	s64 phase_offset;
+	u8 *input_prio;
+	enum ice_cgu_state dpll_state;
+	enum ice_cgu_state prev_dpll_state;
+};
+
+/** ice_dplls - store info required for CCU (clock controlling unit)
+ * @kworker: periodic worker
+ * @work: periodic work
+ * @lock: locks access to configuration of a dpll
+ * @eec: pointer to EEC dpll dev
+ * @pps: pointer to PPS dpll dev
+ * @inputs: input pins pointer
+ * @outputs: output pins pointer
+ * @rclk: recovered pins pointer
+ * @num_inputs: number of input pins available on dpll
+ * @num_outputs: number of output pins available on dpll
+ * @num_rclk: number of recovered clock pins available on dpll
+ * @cgu_state_acq_err_num: number of errors returned during periodic work
+ */
+struct ice_dplls {
+	struct kthread_worker *kworker;
+	struct kthread_delayed_work work;
+	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 *rclk;
+	u32 num_inputs;
+	u32 num_outputs;
+	u8 num_rclk;
+	int cgu_state_acq_err_num;
+};
+
+int ice_dpll_init(struct ice_pf *pf);
+
+void ice_dpll_release(struct ice_pf *pf);
+
+int ice_dpll_rclk_init(struct ice_pf *pf);
+
+void ice_dpll_rclk_release(struct ice_pf *pf);
+
+#endif
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index a9a7f8b52140..8b65f4ad245e 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4896,6 +4896,12 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
 		ice_ptp_init(pf);
 
+	if (ice_is_feature_supported(pf, ICE_F_CGU))
+		ice_dpll_init(pf);
+
+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
+		ice_dpll_rclk_init(pf);
+
 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
 		ice_gnss_init(pf);
 
@@ -5078,6 +5084,10 @@ static void ice_remove(struct pci_dev *pdev)
 		ice_ptp_release(pf);
 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
 		ice_gnss_exit(pf);
+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
+		ice_dpll_rclk_release(pf);
+	if (ice_is_feature_supported(pf, ICE_F_CGU))
+		ice_dpll_release(pf);
 	if (!ice_is_safe_mode(pf))
 		ice_remove_arfs(pf);
 	ice_setup_mc_magic_wake(pf);
-- 
2.30.2


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

* RE: [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
  2023-01-17 18:00 ` Vadim Fedorenko
@ 2023-01-18 18:07   ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-18 18:07 UTC (permalink / raw)
  To: Vadim Fedorenko, Jakub Kicinski, Jiri Pirko, Jonathan Lemon, Paolo Abeni
  Cc: netdev, linux-arm-kernel, linux-clk

>-----Original Message-----
>From: Vadim Fedorenko <vadfed@meta.com>
>Sent: Tuesday, January 17, 2023 7:01 PM
>
>Implement common API for clock/DPLL configuration and status reporting.
>The API utilises netlink interface as transport for commands and event
>notifications. This API aim to extend current pin configuration and make it
>flexible and easy to cover special configurations.
>
>v4 -> v5:
> * fix code issues found during last reviews:
>   - replace cookie with clock id
>	 - follow one naming schema in dpll subsys
>	 - move function comments to dpll_core.c, fix exports
>	 - remove single-use helper functions
>	 - merge device register with alloc
>   - lock and unlock mutex on dpll device release
>   - move dpll_type to uapi header
>   - rename DPLLA_DUMP_FILTER to DPLLA_FILTER
>   - rename dpll_pin_state to dpll_pin_mode
>   - rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
>   - remove DPLL_CHANGE_PIN_TYPE enum value
> * rewrite framework once again (Arkadiusz)
>   - add clock class:
>     Provide userspace with clock class value of DPLL with dpll device dump
>     netlink request. Clock class is assigned by driver allocating a dpll
>     device. Clock class values are defined as specified in:
>     ITU-T G.8273.2/Y.1368.2 recommendation.
>   - dpll device naming schema use new pattern:
>	   "dpll_%s_%d_%d", where:
>       - %s - dev_name(parent) of parent device,
>       - %d (1) - enum value of dpll type,
>       - %d (2) - device index provided by parent device.
>   - new muxed/shared pin registration:
>	   Let the kernel module to register a shared or muxed pin without
>finding
>     it or its parent. Instead use a parent/shared pin description to find
>     correct pin internally in dpll_core, simplifing a dpll API
> * Implement complex DPLL design in ice driver (Arkadiusz)
> * Remove ptp_ocp driver from the series for now
>v3 -> v4:
> * redesign framework to make pins dynamically allocated (Arkadiusz)
> * implement shared pins (Arkadiusz)
>v2 -> v3:
> * implement source select mode (Arkadiusz)
> * add documentation
> * implementation improvements (Jakub)
>v1 -> v2:
> * implement returning supported input/output types
> * ptp_ocp: follow suggestions from Jonathan
> * add linux-clk mailing list
>v0 -> v1:
> * fix code style and errors
> * add linux-arm mailing list
>
>Arkadiusz Kubalewski (2):
>  ice: add admin commands to access cgu configuration
>  ice: implement dpll interface to control cgu
>
>Vadim Fedorenko (2):
>  dpll: documentation on DPLL subsystem interface
>  dpll: Add DPLL framework base functions
>
> Documentation/networking/dpll.rst             |  280 +++
> Documentation/networking/index.rst            |    1 +
> MAINTAINERS                                   |    8 +
> drivers/Kconfig                               |    2 +
> drivers/Makefile                              |    1 +
> drivers/dpll/Kconfig                          |    7 +
> drivers/dpll/Makefile                         |    9 +
> drivers/dpll/dpll_core.c                      | 1010 ++++++++
> drivers/dpll/dpll_core.h                      |  105 +
> drivers/dpll/dpll_netlink.c                   |  883 +++++++
> drivers/dpll/dpll_netlink.h                   |   24 +
> drivers/net/ethernet/intel/Kconfig            |    1 +
> drivers/net/ethernet/intel/ice/Makefile       |    3 +-
> drivers/net/ethernet/intel/ice/ice.h          |    5 +
> .../net/ethernet/intel/ice/ice_adminq_cmd.h   |  240 +-
> drivers/net/ethernet/intel/ice/ice_common.c   |  467 ++++
> drivers/net/ethernet/intel/ice/ice_common.h   |   43 +
> drivers/net/ethernet/intel/ice/ice_dpll.c     | 2115 +++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h     |   99 +
> drivers/net/ethernet/intel/ice/ice_lib.c      |   17 +-
> drivers/net/ethernet/intel/ice/ice_main.c     |   10 +
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c   |  408 ++++
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 ++
> drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
> include/linux/dpll.h                          |  282 +++
> include/uapi/linux/dpll.h                     |  294 +++
> 26 files changed, 6549 insertions(+), 6 deletions(-)  create mode 100644
>Documentation/networking/dpll.rst  create mode 100644 drivers/dpll/Kconfig
>create mode 100644 drivers/dpll/Makefile  create mode 100644
>drivers/dpll/dpll_core.c  create mode 100644 drivers/dpll/dpll_core.h
>create mode 100644 drivers/dpll/dpll_netlink.c  create mode 100644
>drivers/dpll/dpll_netlink.h  create mode 100644
>drivers/net/ethernet/intel/ice/ice_dpll.c
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
> create mode 100644 include/linux/dpll.h  create mode 100644
>include/uapi/linux/dpll.h
>
>--
>2.30.2


Based on today's sync meeting, changes we are going to introduce in next
version:
- reduce the muxed-pin number (artificial multiplication) on list of dpll's
pins, have a single pin which can be connected with multiple parents,
- introduce separated get command for the pin attributes,
- allow infinite name length of dpll device,
- remove a type embedded in dpll's name and introduce new attribute instead,
- remove clock class attribute as it is not known by the driver without
compliance testing on given SW/HW configuration,
- add dpll device "default" quality level attribute, as shall be known
by driver for a given hardware.


BR, Arkadiusz

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

* RE: [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
@ 2023-01-18 18:07   ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-18 18:07 UTC (permalink / raw)
  To: Vadim Fedorenko, Jakub Kicinski, Jiri Pirko, Jonathan Lemon, Paolo Abeni
  Cc: netdev, linux-arm-kernel, linux-clk

>-----Original Message-----
>From: Vadim Fedorenko <vadfed@meta.com>
>Sent: Tuesday, January 17, 2023 7:01 PM
>
>Implement common API for clock/DPLL configuration and status reporting.
>The API utilises netlink interface as transport for commands and event
>notifications. This API aim to extend current pin configuration and make it
>flexible and easy to cover special configurations.
>
>v4 -> v5:
> * fix code issues found during last reviews:
>   - replace cookie with clock id
>	 - follow one naming schema in dpll subsys
>	 - move function comments to dpll_core.c, fix exports
>	 - remove single-use helper functions
>	 - merge device register with alloc
>   - lock and unlock mutex on dpll device release
>   - move dpll_type to uapi header
>   - rename DPLLA_DUMP_FILTER to DPLLA_FILTER
>   - rename dpll_pin_state to dpll_pin_mode
>   - rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
>   - remove DPLL_CHANGE_PIN_TYPE enum value
> * rewrite framework once again (Arkadiusz)
>   - add clock class:
>     Provide userspace with clock class value of DPLL with dpll device dump
>     netlink request. Clock class is assigned by driver allocating a dpll
>     device. Clock class values are defined as specified in:
>     ITU-T G.8273.2/Y.1368.2 recommendation.
>   - dpll device naming schema use new pattern:
>	   "dpll_%s_%d_%d", where:
>       - %s - dev_name(parent) of parent device,
>       - %d (1) - enum value of dpll type,
>       - %d (2) - device index provided by parent device.
>   - new muxed/shared pin registration:
>	   Let the kernel module to register a shared or muxed pin without
>finding
>     it or its parent. Instead use a parent/shared pin description to find
>     correct pin internally in dpll_core, simplifing a dpll API
> * Implement complex DPLL design in ice driver (Arkadiusz)
> * Remove ptp_ocp driver from the series for now
>v3 -> v4:
> * redesign framework to make pins dynamically allocated (Arkadiusz)
> * implement shared pins (Arkadiusz)
>v2 -> v3:
> * implement source select mode (Arkadiusz)
> * add documentation
> * implementation improvements (Jakub)
>v1 -> v2:
> * implement returning supported input/output types
> * ptp_ocp: follow suggestions from Jonathan
> * add linux-clk mailing list
>v0 -> v1:
> * fix code style and errors
> * add linux-arm mailing list
>
>Arkadiusz Kubalewski (2):
>  ice: add admin commands to access cgu configuration
>  ice: implement dpll interface to control cgu
>
>Vadim Fedorenko (2):
>  dpll: documentation on DPLL subsystem interface
>  dpll: Add DPLL framework base functions
>
> Documentation/networking/dpll.rst             |  280 +++
> Documentation/networking/index.rst            |    1 +
> MAINTAINERS                                   |    8 +
> drivers/Kconfig                               |    2 +
> drivers/Makefile                              |    1 +
> drivers/dpll/Kconfig                          |    7 +
> drivers/dpll/Makefile                         |    9 +
> drivers/dpll/dpll_core.c                      | 1010 ++++++++
> drivers/dpll/dpll_core.h                      |  105 +
> drivers/dpll/dpll_netlink.c                   |  883 +++++++
> drivers/dpll/dpll_netlink.h                   |   24 +
> drivers/net/ethernet/intel/Kconfig            |    1 +
> drivers/net/ethernet/intel/ice/Makefile       |    3 +-
> drivers/net/ethernet/intel/ice/ice.h          |    5 +
> .../net/ethernet/intel/ice/ice_adminq_cmd.h   |  240 +-
> drivers/net/ethernet/intel/ice/ice_common.c   |  467 ++++
> drivers/net/ethernet/intel/ice/ice_common.h   |   43 +
> drivers/net/ethernet/intel/ice/ice_dpll.c     | 2115 +++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h     |   99 +
> drivers/net/ethernet/intel/ice/ice_lib.c      |   17 +-
> drivers/net/ethernet/intel/ice/ice_main.c     |   10 +
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c   |  408 ++++
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h   |  240 ++
> drivers/net/ethernet/intel/ice/ice_type.h     |    1 +
> include/linux/dpll.h                          |  282 +++
> include/uapi/linux/dpll.h                     |  294 +++
> 26 files changed, 6549 insertions(+), 6 deletions(-)  create mode 100644
>Documentation/networking/dpll.rst  create mode 100644 drivers/dpll/Kconfig
>create mode 100644 drivers/dpll/Makefile  create mode 100644
>drivers/dpll/dpll_core.c  create mode 100644 drivers/dpll/dpll_core.h
>create mode 100644 drivers/dpll/dpll_netlink.c  create mode 100644
>drivers/dpll/dpll_netlink.h  create mode 100644
>drivers/net/ethernet/intel/ice/ice_dpll.c
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
> create mode 100644 include/linux/dpll.h  create mode 100644
>include/uapi/linux/dpll.h
>
>--
>2.30.2


Based on today's sync meeting, changes we are going to introduce in next
version:
- reduce the muxed-pin number (artificial multiplication) on list of dpll's
pins, have a single pin which can be connected with multiple parents,
- introduce separated get command for the pin attributes,
- allow infinite name length of dpll device,
- remove a type embedded in dpll's name and introduce new attribute instead,
- remove clock class attribute as it is not known by the driver without
compliance testing on given SW/HW configuration,
- add dpll device "default" quality level attribute, as shall be known
by driver for a given hardware.


BR, Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
  2023-01-18 18:07   ` Kubalewski, Arkadiusz
@ 2023-01-19  0:15     ` Jakub Kicinski
  -1 siblings, 0 replies; 44+ messages in thread
From: Jakub Kicinski @ 2023-01-19  0:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk

On Wed, 18 Jan 2023 18:07:53 +0000 Kubalewski, Arkadiusz wrote:
> Based on today's sync meeting, changes we are going to introduce in next
> version:
> - reduce the muxed-pin number (artificial multiplication) on list of dpll's
> pins, have a single pin which can be connected with multiple parents,
> - introduce separated get command for the pin attributes,
> - allow infinite name length of dpll device,
> - remove a type embedded in dpll's name and introduce new attribute instead,
> - remove clock class attribute as it is not known by the driver without
> compliance testing on given SW/HW configuration,
> - add dpll device "default" quality level attribute, as shall be known
> by driver for a given hardware.

I converted the patches to use the spec, and pushed them out here:

https://github.com/kuba-moo/ynl/tree/dpll

I kept the conversion step-by-step to help the readers a bit but
the conversion patches should all be squashed into the main DPLL patch.

The patches are on top of my YNL branch ('main' in that repo). 
I'll post the YNL patches later today, so hopefully very soon they will
be upstream.

Two major pieces of work which I didn't do for DPLL:
 - docs - I dropped most of the kdocs, the copy-n-pasting was too much;
   if you want to keep the docs in the uAPI you need to add the
   appropriate stuff in the spec (look at the definition of
   pin-signal-type for an example of a fully documented enum)
 - the notifications are quite unorthodox in the current 
   implementation, so I faked the enums :S
   Usually the notification is the same as the response to a get.
   IIRC 'notify' and 'event' operation types should be used in the spec.

There is documentation on the specs in
Documentation/userspace-api/netlink/ which should give some idea of how
things work. There is also another example of a spec here:
https://github.com/kuba-moo/ynl/blob/psp/Documentation/netlink/specs/psp.yaml

To regenerate the C code after changes to YAML:

  ./tools/net/ynl/ynl-regen.sh

if the Python script doing the generation dies and eats the files -
bring them back with:

  git checkout drivers/dpll/dpll_nl.c drivers/dpll/dpll_nl.h \
               include/uapi/linux/dpll.h

There is a "universal CLI" script in:

  ./tools/net/ynl/samples/cli.py

which should be able to take in JSON requests and output JSON responses.
I'm improvising, because I don't have any implementation to try it 
out, but something like:

  ./tools/net/ynl/samples/cli.py \
       --spec Documentation/netlink/specs/dpll.yaml \
       --do device-get --json '{"id": 1}'

should pretty print the info about device with id 1. Actually - it
probably won't because I didn't fill in all the attrs in the pin nest.
But with a bit more work on the spec it should work.

Would you be able to finish this conversion. Just LMK if you have any
problems, the edges are definitely very sharp at this point.

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

* Re: [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
@ 2023-01-19  0:15     ` Jakub Kicinski
  0 siblings, 0 replies; 44+ messages in thread
From: Jakub Kicinski @ 2023-01-19  0:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk

On Wed, 18 Jan 2023 18:07:53 +0000 Kubalewski, Arkadiusz wrote:
> Based on today's sync meeting, changes we are going to introduce in next
> version:
> - reduce the muxed-pin number (artificial multiplication) on list of dpll's
> pins, have a single pin which can be connected with multiple parents,
> - introduce separated get command for the pin attributes,
> - allow infinite name length of dpll device,
> - remove a type embedded in dpll's name and introduce new attribute instead,
> - remove clock class attribute as it is not known by the driver without
> compliance testing on given SW/HW configuration,
> - add dpll device "default" quality level attribute, as shall be known
> by driver for a given hardware.

I converted the patches to use the spec, and pushed them out here:

https://github.com/kuba-moo/ynl/tree/dpll

I kept the conversion step-by-step to help the readers a bit but
the conversion patches should all be squashed into the main DPLL patch.

The patches are on top of my YNL branch ('main' in that repo). 
I'll post the YNL patches later today, so hopefully very soon they will
be upstream.

Two major pieces of work which I didn't do for DPLL:
 - docs - I dropped most of the kdocs, the copy-n-pasting was too much;
   if you want to keep the docs in the uAPI you need to add the
   appropriate stuff in the spec (look at the definition of
   pin-signal-type for an example of a fully documented enum)
 - the notifications are quite unorthodox in the current 
   implementation, so I faked the enums :S
   Usually the notification is the same as the response to a get.
   IIRC 'notify' and 'event' operation types should be used in the spec.

There is documentation on the specs in
Documentation/userspace-api/netlink/ which should give some idea of how
things work. There is also another example of a spec here:
https://github.com/kuba-moo/ynl/blob/psp/Documentation/netlink/specs/psp.yaml

To regenerate the C code after changes to YAML:

  ./tools/net/ynl/ynl-regen.sh

if the Python script doing the generation dies and eats the files -
bring them back with:

  git checkout drivers/dpll/dpll_nl.c drivers/dpll/dpll_nl.h \
               include/uapi/linux/dpll.h

There is a "universal CLI" script in:

  ./tools/net/ynl/samples/cli.py

which should be able to take in JSON requests and output JSON responses.
I'm improvising, because I don't have any implementation to try it 
out, but something like:

  ./tools/net/ynl/samples/cli.py \
       --spec Documentation/netlink/specs/dpll.yaml \
       --do device-get --json '{"id": 1}'

should pretty print the info about device with id 1. Actually - it
probably won't because I didn't fill in all the attrs in the pin nest.
But with a bit more work on the spec it should work.

Would you be able to finish this conversion. Just LMK if you have any
problems, the edges are definitely very sharp at this point.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
  2023-01-19  0:15     ` Jakub Kicinski
@ 2023-01-19 11:48       ` Jiri Pirko
  -1 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-19 11:48 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk

Thu, Jan 19, 2023 at 01:15:25AM CET, kuba@kernel.org wrote:
>On Wed, 18 Jan 2023 18:07:53 +0000 Kubalewski, Arkadiusz wrote:
>> Based on today's sync meeting, changes we are going to introduce in next
>> version:
>> - reduce the muxed-pin number (artificial multiplication) on list of dpll's
>> pins, have a single pin which can be connected with multiple parents,
>> - introduce separated get command for the pin attributes,
>> - allow infinite name length of dpll device,
>> - remove a type embedded in dpll's name and introduce new attribute instead,
>> - remove clock class attribute as it is not known by the driver without
>> compliance testing on given SW/HW configuration,
>> - add dpll device "default" quality level attribute, as shall be known
>> by driver for a given hardware.
>
>I converted the patches to use the spec, and pushed them out here:
>
>https://github.com/kuba-moo/ynl/tree/dpll
>
>I kept the conversion step-by-step to help the readers a bit but
>the conversion patches should all be squashed into the main DPLL patch.
>
>The patches are on top of my YNL branch ('main' in that repo). 
>I'll post the YNL patches later today, so hopefully very soon they will
>be upstream.
>
>Two major pieces of work which I didn't do for DPLL:
> - docs - I dropped most of the kdocs, the copy-n-pasting was too much;
>   if you want to keep the docs in the uAPI you need to add the
>   appropriate stuff in the spec (look at the definition of
>   pin-signal-type for an example of a fully documented enum)
> - the notifications are quite unorthodox in the current 
>   implementation, so I faked the enums :S
>   Usually the notification is the same as the response to a get.
>   IIRC 'notify' and 'event' operation types should be used in the spec.

I already pointed this out in the past. This is not he only thing that
was ignored during the dpll review. I have to say I'm a bit annoyed by
that.


>
>There is documentation on the specs in
>Documentation/userspace-api/netlink/ which should give some idea of how
>things work. There is also another example of a spec here:
>https://github.com/kuba-moo/ynl/blob/psp/Documentation/netlink/specs/psp.yaml
>
>To regenerate the C code after changes to YAML:
>
>  ./tools/net/ynl/ynl-regen.sh
>
>if the Python script doing the generation dies and eats the files -
>bring them back with:
>
>  git checkout drivers/dpll/dpll_nl.c drivers/dpll/dpll_nl.h \
>               include/uapi/linux/dpll.h
>
>There is a "universal CLI" script in:
>
>  ./tools/net/ynl/samples/cli.py
>
>which should be able to take in JSON requests and output JSON responses.
>I'm improvising, because I don't have any implementation to try it 
>out, but something like:
>
>  ./tools/net/ynl/samples/cli.py \
>       --spec Documentation/netlink/specs/dpll.yaml \
>       --do device-get --json '{"id": 1}'
>
>should pretty print the info about device with id 1. Actually - it
>probably won't because I didn't fill in all the attrs in the pin nest.
>But with a bit more work on the spec it should work.
>
>Would you be able to finish this conversion. Just LMK if you have any
>problems, the edges are definitely very sharp at this point.

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

* Re: [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
@ 2023-01-19 11:48       ` Jiri Pirko
  0 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-19 11:48 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk

Thu, Jan 19, 2023 at 01:15:25AM CET, kuba@kernel.org wrote:
>On Wed, 18 Jan 2023 18:07:53 +0000 Kubalewski, Arkadiusz wrote:
>> Based on today's sync meeting, changes we are going to introduce in next
>> version:
>> - reduce the muxed-pin number (artificial multiplication) on list of dpll's
>> pins, have a single pin which can be connected with multiple parents,
>> - introduce separated get command for the pin attributes,
>> - allow infinite name length of dpll device,
>> - remove a type embedded in dpll's name and introduce new attribute instead,
>> - remove clock class attribute as it is not known by the driver without
>> compliance testing on given SW/HW configuration,
>> - add dpll device "default" quality level attribute, as shall be known
>> by driver for a given hardware.
>
>I converted the patches to use the spec, and pushed them out here:
>
>https://github.com/kuba-moo/ynl/tree/dpll
>
>I kept the conversion step-by-step to help the readers a bit but
>the conversion patches should all be squashed into the main DPLL patch.
>
>The patches are on top of my YNL branch ('main' in that repo). 
>I'll post the YNL patches later today, so hopefully very soon they will
>be upstream.
>
>Two major pieces of work which I didn't do for DPLL:
> - docs - I dropped most of the kdocs, the copy-n-pasting was too much;
>   if you want to keep the docs in the uAPI you need to add the
>   appropriate stuff in the spec (look at the definition of
>   pin-signal-type for an example of a fully documented enum)
> - the notifications are quite unorthodox in the current 
>   implementation, so I faked the enums :S
>   Usually the notification is the same as the response to a get.
>   IIRC 'notify' and 'event' operation types should be used in the spec.

I already pointed this out in the past. This is not he only thing that
was ignored during the dpll review. I have to say I'm a bit annoyed by
that.


>
>There is documentation on the specs in
>Documentation/userspace-api/netlink/ which should give some idea of how
>things work. There is also another example of a spec here:
>https://github.com/kuba-moo/ynl/blob/psp/Documentation/netlink/specs/psp.yaml
>
>To regenerate the C code after changes to YAML:
>
>  ./tools/net/ynl/ynl-regen.sh
>
>if the Python script doing the generation dies and eats the files -
>bring them back with:
>
>  git checkout drivers/dpll/dpll_nl.c drivers/dpll/dpll_nl.h \
>               include/uapi/linux/dpll.h
>
>There is a "universal CLI" script in:
>
>  ./tools/net/ynl/samples/cli.py
>
>which should be able to take in JSON requests and output JSON responses.
>I'm improvising, because I don't have any implementation to try it 
>out, but something like:
>
>  ./tools/net/ynl/samples/cli.py \
>       --spec Documentation/netlink/specs/dpll.yaml \
>       --do device-get --json '{"id": 1}'
>
>should pretty print the info about device with id 1. Actually - it
>probably won't because I didn't fill in all the attrs in the pin nest.
>But with a bit more work on the spec it should work.
>
>Would you be able to finish this conversion. Just LMK if you have any
>problems, the edges are definitely very sharp at this point.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
  2023-01-17 18:00   ` Vadim Fedorenko
@ 2023-01-19 14:54     ` Jiri Pirko
  -1 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-19 14:54 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk, Milena Olech,
	Michal Michalik

Tue, Jan 17, 2023 at 07:00:51PM CET, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>Control over clock generation unit is required for further development
>of Synchronous Ethernet feature. Interface provides ability to obtain
>current state of a dpll, its sources and outputs which are pins, and
>allows their configuration.
>
>Co-developed-by: Milena Olech <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
>---
> drivers/net/ethernet/intel/Kconfig        |    1 +
> drivers/net/ethernet/intel/ice/Makefile   |    3 +-
> drivers/net/ethernet/intel/ice/ice.h      |    4 +
> drivers/net/ethernet/intel/ice/ice_dpll.c | 2115 +++++++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h |   99 +
> drivers/net/ethernet/intel/ice/ice_main.c |   10 +
> 6 files changed, 2231 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
>
>diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
>index 3facb55b7161..dcf0e12991bf 100644
>--- a/drivers/net/ethernet/intel/Kconfig
>+++ b/drivers/net/ethernet/intel/Kconfig
>@@ -300,6 +300,7 @@ config ICE
> 	select DIMLIB
> 	select NET_DEVLINK
> 	select PLDMFW
>+	select DPLL
> 	help
> 	  This driver supports Intel(R) Ethernet Connection E800 Series of
> 	  devices.  For more information on how to identify your adapter, go
>diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
>index 9183d480b70b..ebfd456f76cd 100644
>--- a/drivers/net/ethernet/intel/ice/Makefile
>+++ b/drivers/net/ethernet/intel/ice/Makefile
>@@ -32,7 +32,8 @@ ice-y := ice_main.o	\
> 	 ice_lag.o	\
> 	 ice_ethtool.o  \
> 	 ice_repr.o	\
>-	 ice_tc_lib.o
>+	 ice_tc_lib.o	\
>+	 ice_dpll.o
> ice-$(CONFIG_PCI_IOV) +=	\
> 	ice_sriov.o		\
> 	ice_virtchnl.o		\
>diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
>index 3368dba42789..efc1844ef77c 100644
>--- a/drivers/net/ethernet/intel/ice/ice.h
>+++ b/drivers/net/ethernet/intel/ice/ice.h
>@@ -73,6 +73,7 @@
> #include "ice_lag.h"
> #include "ice_vsi_vlan_ops.h"
> #include "ice_gnss.h"
>+#include "ice_dpll.h"
> 
> #define ICE_BAR0		0
> #define ICE_REQ_DESC_MULTIPLE	32
>@@ -198,6 +199,7 @@
> enum ice_feature {
> 	ICE_F_DSCP,
> 	ICE_F_PTP_EXTTS,
>+	ICE_F_PHY_RCLK,
> 	ICE_F_SMA_CTRL,
> 	ICE_F_CGU,
> 	ICE_F_GNSS,
>@@ -509,6 +511,7 @@ enum ice_pf_flags {
> 	ICE_FLAG_PLUG_AUX_DEV,
> 	ICE_FLAG_MTU_CHANGED,
> 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
>+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
> 	ICE_PF_FLAGS_NBITS		/* must be last */
> };
> 
>@@ -633,6 +636,7 @@ struct ice_pf {
> #define ICE_VF_AGG_NODE_ID_START	65
> #define ICE_MAX_VF_AGG_NODES		32
> 	struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
>+	struct ice_dplls dplls;
> };
> 
> struct ice_netdev_priv {
>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
>new file mode 100644
>index 000000000000..6ed1fcee4e03
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>@@ -0,0 +1,2115 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#include "ice.h"
>+#include "ice_lib.h"
>+#include "ice_trace.h"
>+#include <linux/dpll.h>
>+#include <uapi/linux/dpll.h>

Don't include uapi header directly. Inlude of linux/dpll.h is enough.


>+
>+#define CGU_STATE_ACQ_ERR_THRESHOLD	50
>+#define ICE_DPLL_FREQ_1_HZ		1
>+#define ICE_DPLL_FREQ_10_MHZ		10000000
>+
>+/**
>+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock status
>+ */
>+static const enum dpll_lock_status
>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {

Don't use __ define.


>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>+};
>+
>+/**
>+ * ice_dpll_pin_type - enumerate ince pin types
>+ */
>+enum ice_dpll_pin_type {
>+	ICE_DPLL_PIN_INVALID = 0,
>+	ICE_DPLL_PIN_TYPE_SOURCE,
>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
>+};
>+
>+/**
>+ * pin_type_name - string names of ice pin types
>+ */
>+static const char * const pin_type_name[] = {
>+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
>+};
>+
>+/**
>+ * ice_dpll_pin_signal_type_to_freq - translate pin_signal_type to freq value
>+ * @sig_type: signal type to find frequency
>+ * @freq: on success - frequency of a signal type
>+ *
>+ * Return:
>+ * * 0 - frequency valid
>+ * * negative - error
>+ */
>+static inline int
>+ice_dpll_pin_signal_type_to_freq(const enum dpll_pin_signal_type sig_type,
>+				 u32 *freq)
>+{
>+	if (sig_type == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>+		return -EINVAL;
>+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_1_PPS)
>+		*freq = ICE_DPLL_FREQ_1_HZ;
>+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_10_MHZ)
>+		*freq = ICE_DPLL_FREQ_10_MHZ;
>+	else
>+		return -EINVAL;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_pin_freq_to_signal_type - translate pin freq to signal type
>+ * @freq: frequency to translate
>+ *
>+ * Return: signal type of a pin based on frequency.
>+ */
>+static inline enum dpll_pin_signal_type
>+ice_dpll_pin_freq_to_signal_type(u32 freq)
>+{
>+	if (freq == ICE_DPLL_FREQ_1_HZ)
>+		return DPLL_PIN_SIGNAL_TYPE_1_PPS;
>+	else if (freq == ICE_DPLL_FREQ_10_MHZ)
>+		return DPLL_PIN_SIGNAL_TYPE_10_MHZ;
>+	else
>+		return DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ;
>+}
>+
>+/**
>+ * ice_dpll_pin_signal_type_set - set pin's signal type in hardware
>+ * @pf: Board private structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being configured
>+ * @sig_type: signal type to be set
>+ *
>+ * Translate pin signal type to frequency and set it on a pin.
>+ *
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+__ice_dpll_pin_signal_type_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+			       const enum ice_dpll_pin_type pin_type,
>+			       const enum dpll_pin_signal_type sig_type)
>+{
>+	u8 flags;
>+	u32 freq;
>+	int ret;
>+
>+	ret = ice_dpll_pin_signal_type_to_freq(sig_type, &freq);
>+	if (ret)
>+		return ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>+					       pin->flags, freq, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags = pin->flags | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>+						0, freq, 0);
>+	} else {
>+		ret = -EINVAL;
>+	}
>+
>+	if (ret) {
>+		dev_dbg(ice_pf_to_dev(pf),
>+			"err:%d %s failed to set pin freq:%u on pin:%u\n",
>+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>+			freq, pin->idx);
>+	} else {
>+		pin->signal_type = sig_type;
>+		pin->freq = freq;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_enable - enable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being enabled
>+ *
>+ * Enable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    const enum ice_dpll_pin_type pin_type)
>+{
>+	u8 flags = pin->flags;
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		flags |= ICE_DPLL_RCLK_SOURCE_FLAG_EN;
>+		ret = 0;
>+	}
>+	if (ret)
>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+			"err:%d %s failed to enable %s pin:%u\n",
>+			ret, ice_aq_str(hw->adminq.sq_last_status),
>+			pin_type_name[pin_type], pin->idx);
>+	else
>+		pin->flags = flags;
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_disable - disable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being disabled
>+ *
>+ * Disable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		     enum ice_dpll_pin_type pin_type)
>+{
>+	u8 flags = pin->flags;
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		flags &= ~(ICE_DPLL_RCLK_SOURCE_FLAG_EN);
>+		ret = ice_aq_set_phy_rec_clk_out(hw, pin->idx, false,
>+						 &pin->freq);
>+	}
>+
>+	if (ret)
>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+			"err:%d %s failed to disable %s pin:%u\n",
>+			ret, ice_aq_str(hw->adminq.sq_last_status),
>+			pin_type_name[pin_type], pin->idx);
>+	else
>+		pin->flags = flags;
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_update - update pin's mode
>+ * @hw: private board struct
>+ * @pin: structure with pin attributes to be updated
>+ * @pin_type: type of pin being updated
>+ *
>+ * Determine pin current mode, frequency and signal type. Then update struct
>+ * holding the pin info.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+int
>+ice_dpll_pin_update(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    const enum ice_dpll_pin_type pin_type)
>+{
>+	int ret;
>+
>+	pin->mode_mask = 0;
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		ret = ice_aq_get_input_pin_cfg(hw, pin->idx, NULL, NULL, NULL,
>+					       &pin->flags, &pin->freq, NULL);
>+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
>+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags)
>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>+		else
>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		ret = ice_aq_get_output_pin_cfg(hw, pin->idx, &pin->flags,
>+						NULL, &pin->freq, NULL);
>+		set_bit(DPLL_PIN_MODE_OUTPUT, &pin->mode_mask);
>+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags)
>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>+		else
>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
>+		if (ICE_DPLL_RCLK_SOURCE_FLAG_EN & pin->flags)
>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>+		else
>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>+		ret = 0;
>+	}
>+	pin->signal_type = ice_dpll_pin_freq_to_signal_type(pin->freq);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_mode_set - set pin's mode
>+ * @pf: Board private structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of modified pin
>+ * @mode: requested mode
>+ *
>+ * Determine requested pin mode set it on a pin.
>+ *
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_mode_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+		      const enum ice_dpll_pin_type pin_type,
>+		      const enum dpll_pin_mode mode)
>+{
>+	int ret;
>+
>+	if (!test_bit(mode, &pin->mode_supported_mask))
>+		return -EINVAL;
>+
>+	if (test_bit(mode, &pin->mode_mask))
>+		return 0;
>+
>+	if (mode == DPLL_PIN_MODE_CONNECTED)
>+		ret = ice_dpll_pin_enable(&pf->hw, pin, pin_type);
>+	else if (mode == DPLL_PIN_MODE_DISCONNECTED)
>+		ret = ice_dpll_pin_disable(&pf->hw, pin, pin_type);
>+	else
>+		ret = -EINVAL;
>+
>+	if (!ret)
>+		ret = ice_dpll_pin_update(&pf->hw, pin, pin_type);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_find_dpll - find ice_dpll on a pf
>+ * @pf: private board structure
>+ * @dpll: kernel's dpll_device pointer to be searched
>+ *
>+ * Return:
>+ * * pointer if ice_dpll with given device dpll pointer is found
>+ * * NULL if not found
>+ */
>+static struct ice_dpll
>+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
>+{
>+	if (!pf || !dpll)
>+		return NULL;
>+
>+	return (dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
>+		dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL);

Return is not a function, don't use ()'s here.


>+}
>+
>+/**
>+ * ice_find_pin - find ice_dpll_pin on a pf
>+ * @pf: private board structure
>+ * @pin: kernel's dpll_pin pointer to be searched for
>+ * @pin_type: type of pins to be searched for
>+ *
>+ * Find and return internal ice pin info pointer holding data of given dpll subsystem
>+ * pin pointer.
>+ *
>+ * Return:
>+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer was
>+ * found in pf internal pin data.
>+ * * NULL - if pin was not found.
>+ */
>+static struct ice_dpll_pin
>+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
>+	      enum ice_dpll_pin_type pin_type)
>+
>+{
>+	struct ice_dpll_pin *pins;
>+	int pin_num, i;
>+
>+	if (!pin || !pf)
>+		return NULL;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		pins = pf->dplls.inputs;
>+		pin_num = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		pins = pf->dplls.outputs;
>+		pin_num = pf->dplls.num_outputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		pins = pf->dplls.rclk;
>+		pin_num = pf->dplls.num_rclk;
>+	} else {
>+		return NULL;
>+	}
>+
>+	for (i = 0; i < pin_num; i++)
>+		if (pin == pins[i].pin)
>+			return &pins[i];
>+
>+	return NULL;
>+}
>+
>+/**
>+ * ice_dpll_hw_source_prio_set - set source priority value in hardware
>+ * @pf: board private structure
>+ * @dpll: ice dpll pointer
>+ * @pin: ice pin pointer
>+ * @prio: priority value being set on a dpll
>+ *
>+ * Internal wrapper for setting the priority in the hardware.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_hw_source_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
>+			    struct ice_dpll_pin *pin, const u32 prio)
>+{
>+	int ret;
>+
>+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
>+				      (u8)prio);
>+	if (ret)
>+		dev_dbg(ice_pf_to_dev(pf),
>+			"err:%d %s failed to set pin prio:%u on pin:%u\n",
>+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>+			prio, pin->idx);
>+	else
>+		dpll->input_prio[pin->idx] = prio;
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_lock_status_get - get dpll lock status callback
>+ * @dpll: registered dpll pointer
>+ * @status: on success holds dpll's lock status
>+ *
>+ * Dpll subsystem callback, provides dpll's lock status.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_lock_status_get(const struct dpll_device *dpll,
>+				    enum dpll_lock_status *status)
>+{
>+	struct ice_pf *pf = dpll_priv(dpll);
>+	struct ice_dpll *d;
>+
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		return -EFAULT;
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
>+	mutex_lock(&pf->dplls.lock);
>+	*status = ice_dpll_status[d->dpll_state];
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_source_idx_get - get dpll's source index
>+ * @dpll: registered dpll pointer
>+ * @pin_idx: on success holds currently selected source pin index
>+ *
>+ * Dpll subsystem callback. Provides index of a source dpll is trying to lock
>+ * with.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_idx_get(const struct dpll_device *dpll, u32 *pin_idx)

Should return struct dpll_pin *. That should be the consitent driver api
handle for pin.


>+{
>+	struct ice_pf *pf = dpll_priv(dpll);
>+	struct ice_dpll *d;
>+	int ret = 0;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	*pin_idx = (u32)d->source_idx;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p d:%p, idx:%u\n",
>+		__func__, dpll, pf, d, *pin_idx);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_mode_get - get dpll's working mode
>+ * @dpll: registered dpll pointer
>+ * @mode: on success holds current working mode of dpll
>+ *
>+ * Dpll subsystem callback. Provides working mode of dpll.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_mode_get(const struct dpll_device *dpll,
>+			     enum dpll_mode *mode)
>+{
>+	struct ice_pf *pf = dpll_priv(dpll);
>+	struct ice_dpll *d;
>+	int ret = 0;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		ret = -EFAULT;
>+	else
>+		*mode = DPLL_MODE_AUTOMATIC;
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_mode_get - check if dpll's working mode is supported
>+ * @dpll: registered dpll pointer
>+ * @mode: mode to be checked for support
>+ *
>+ * Dpll subsystem callback. Provides information if working mode is supported
>+ * by dpll.
>+ *
>+ * Return:
>+ * * true - mode is supported
>+ * * false - mode is not supported
>+ */
>+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
>+				    const enum dpll_mode mode)
>+{
>+	struct ice_pf *pf = dpll_priv(dpll);
>+	struct ice_dpll *d;
>+	bool ret = true;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		ret = false;
>+	else
>+		if (mode != DPLL_MODE_AUTOMATIC)

"else if" on a single line.


>+			ret = false;
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_signal_type_supported - if pin signal type is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type being checked for support
>+ * @pin_type: type of a pin being checked
>+ *
>+ * Check is signal type is supported on given pin/dpll pair.
>+ *
>+ * Return:
>+ * * true - supported
>+ * * false - not supported
>+ */
>+static bool
>+ice_dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>+				   const struct dpll_pin *pin,
>+				   const enum dpll_pin_signal_type sig_type,
>+				   const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	bool supported = false;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (p) {
>+		if (test_bit(sig_type, &p->signal_type_mask))

Loose the nested if.


>+			supported = true;
>+	}
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return supported;
>+}
>+
>+/**
>+ * ice_dpll_rclk_signal_type_supported - if rclk source signal type is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type being checked for support
>+ *
>+ * Dpll subsystem callback. Check is signal type is supported on given pin/dpll
>+ * pair.
>+ *
>+ * Return:
>+ * * true - supported
>+ * * false - not supported
>+ */
>+static bool
>+ice_dpll_rclk_signal_type_supported(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin,
>+				    const enum dpll_pin_signal_type sig_type)
>+{
>+	const enum ice_dpll_pin_type t = ICE_DPLL_PIN_TYPE_RCLK_SOURCE;
>+
>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type, t);
>+}
>+
>+/**
>+ * ice_dpll_source_signal_type_supported - if source signal type is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type being checked for support
>+ *
>+ * Dpll subsystem callback. Check is signal type is supported on given pin/dpll
>+ * pair.
>+ *
>+ * Return:
>+ * * true - supported
>+ * * false - not supported
>+ */
>+static bool
>+ice_dpll_source_signal_type_supported(const struct dpll_device *dpll,
>+				      const struct dpll_pin *pin,
>+				      const enum dpll_pin_signal_type sig_type)
>+{
>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
>+						  ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_output_signal_type_supported - if output pin signal type is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type being checked
>+ *
>+ * Dpll subsystem callback. Check if signal type is supported on given pin/dpll
>+ * pair.
>+ *
>+ * Return:
>+ * * true - supported
>+ * * false - not supported
>+ */
>+static bool
>+ice_dpll_output_signal_type_supported(const struct dpll_device *dpll,
>+				      const struct dpll_pin *pin,
>+				      const enum dpll_pin_signal_type sig_type)
>+{
>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
>+						  ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_signal_type_get - get dpll's pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: on success - current signal type used on the pin
>+ * @pin_type: type of a pin being checked
>+ *
>+ * Find a pin and assign sig_type with its current signal type value.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_pin_signal_type_get(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					enum dpll_pin_signal_type *sig_type,
>+					const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	int ret = -ENODEV;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (p) {
>+		*sig_type = p->signal_type;
>+		ret = 0;
>+	}
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p\n",
>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_signal_type_get - get dpll's output pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: on success - current signal type value used on the pin
>+ *
>+ * Dpll subsystem callback. Find current signal type of given pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_output_signal_type_get(const struct dpll_device *dpll,
>+					   const struct dpll_pin *pin,
>+					   enum dpll_pin_signal_type *sig_type)
>+{
>+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_signal_type_set - set dpll pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type to be set
>+ * @pin_type: type of a pin being configured
>+ *
>+ * Handler for signal type modification on pins. Set signal type value for
>+ * a given pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_pin_signal_type_set(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					const enum dpll_pin_signal_type sig_type,
>+					const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	int ret = -EFAULT;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p)
>+		goto unlock;
>+	if (test_bit(sig_type, &p->signal_type_mask))
>+		ret = __ice_dpll_pin_signal_type_set(pf, p, pin_type, sig_type);
>+	else
>+		ret = -EINVAL;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL,  ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_signal_type_set - set dpll output pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type to be set
>+ *
>+ * Dpll subsystem callback. Wraps signal type modification handler.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_output_signal_type_set(const struct dpll_device *dpll,
>+				const struct dpll_pin *pin,
>+				const enum dpll_pin_signal_type sig_type)
>+{
>+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_mode_enable - enables a pin mode
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be set
>+ * @pin_type: type of pin being modified
>+ *
>+ * Handler for enabling the pin mode.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_pin_mode_enable(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin,
>+				    const enum dpll_pin_mode mode,
>+				    const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	int ret = -EFAULT;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (p)
>+		ret = ice_dpll_pin_mode_set(pf, p, pin_type, mode);
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL, ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_mode_enable - enable rclk-source pin mode
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be set
>+ *
>+ * Dpll subsystem callback. Enables given mode on recovered clock source type
>+ * pin.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_rclk_mode_enable(const struct dpll_device *dpll,
>+				     const struct dpll_pin *pin,
>+				     const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_output_mode_enable - enable output pin mode
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be set
>+ *
>+ * Dpll subsystem callback. Enables given mode on output type pin.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_output_mode_enable(const struct dpll_device *dpll,
>+				       const struct dpll_pin *pin,
>+				       const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>+					 ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_mode_enable - enable source pin mode
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be set
>+ *
>+ * Dpll subsystem callback. Enables given mode on source type pin.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_source_mode_enable(const struct dpll_device *dpll,
>+				       const struct dpll_pin *pin,
>+				       const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>+					 ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_source_signal_type_get - get source pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @type: on success - source pin signal type
>+ *
>+ * Dpll subsystem callback. Get source pin signal type value.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_signal_type_get(const struct dpll_device *dpll,
>+					   const struct dpll_pin *pin,
>+					   enum dpll_pin_signal_type *sig_type)
>+{
>+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_source_signal_type_set - set dpll output pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type to be set
>+ *
>+ * dpll subsystem callback. Set source pin signal type value.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_source_signal_type_set(const struct dpll_device *dpll,
>+				const struct dpll_pin *pin,
>+				const enum dpll_pin_signal_type sig_type)
>+{
>+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_source_prio_get - get dpll's source prio
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @prio: on success - returns source priority on dpll
>+ *
>+ * Dpll subsystem callback. Handler for getting priority of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_prio_get(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin, u32 *prio)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll *d = NULL;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (*prio > ICE_DPLL_PRIO_MAX)
>+		return ret;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (!p)
>+		goto unlock;
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		goto unlock;
>+	*prio = d->input_prio[p->idx];
>+	ret = 0;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>+		__func__, dpll, pin, pf, ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_source_prio_set - set dpll source prio
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @prio: source priority to be set on dpll
>+ *
>+ * Dpll subsystem callback. Handler for setting priority of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_prio_set(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin, const u32 prio)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll *d = NULL;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (prio > ICE_DPLL_PRIO_MAX)
>+		return ret;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (!p)
>+		goto unlock;
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		goto unlock;
>+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>+		__func__, dpll, pin, pf, ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_mode_active -  check if given pin's mode is active
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ * @pin_type: type of a pin to be checked
>+ *
>+ * Handler for checking if given mode is active on a pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_pin_mode_active(const struct dpll_device *dpll,
>+				     const struct dpll_pin *pin,
>+				     const enum dpll_pin_mode mode,
>+				     const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	bool ret = false;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p)
>+		goto unlock;
>+	if (test_bit(mode, &p->mode_mask))
>+		ret = true;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_mode_active - check if rclk source pin's mode is active
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
>+ * on a recovered clock pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_rclk_mode_active(const struct dpll_device *dpll,
>+				      const struct dpll_pin *pin,
>+				      const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_output_mode_active - check if output pin's mode is active
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
>+ * on an output pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_output_mode_active(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>+					ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_mode_active - check if source pin's mode is active
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
>+ * on a source pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_source_mode_active(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>+					ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_pin_mode_supported - check if pin's mode is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ * @pin_type: type of a pin being checked
>+ *
>+ * Handler for checking if given mode is supported on a pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_pin_mode_supported(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					const enum dpll_pin_mode mode,
>+					const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	bool ret = false;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+
>+	if (!p)
>+		goto unlock;
>+	if (test_bit(mode, &p->mode_supported_mask))
>+		ret = true;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_mode_supported - check if rclk pin's mode is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>+ * supported on a clock recovery pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_rclk_mode_supported(const struct dpll_device *dpll,
>+					 const struct dpll_pin *pin,
>+					 const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>+					    ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_output_mode_supported - check if output pin's mode is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>+ * supported on an output pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_output_mode_supported(const struct dpll_device *dpll,
>+					   const struct dpll_pin *pin,
>+					   const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_mode_supported - check if source pin's mode is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>+ * supported on a source pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_source_mode_supported(const struct dpll_device *dpll,
>+					   const struct dpll_pin *pin,
>+					   const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_rclk_pin_sig_type_get - get signal type of rclk pin
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: on success - holds a signal type of a pin
>+ *
>+ * DPLL subsystem callback, provides signal type of clock recovery pin.
>+ *
>+ * Return:
>+ * * 0 - success, sig_type value is valid
>+ * * negative - error
>+ */
>+static int ice_dpll_rclk_pin_sig_type_get(const struct dpll_device *dpll,
>+					  const struct dpll_pin *pin,
>+					  enum dpll_pin_signal_type *sig_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	int ret = 0;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (!p) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	*sig_type = p->signal_type;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p sig_type:%d ret:%d\n",
>+		__func__, dpll, pin, pf, *sig_type, ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_pin_net_if_index_get - get OS interface index callback
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @net_if_idx: on success - holds OS interface index
>+ *
>+ * dpll subsystem callback, obtains OS interface index and pass to the caller.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_rclk_pin_net_if_index_get(const struct dpll_device *dpll,
>+					      const struct dpll_pin *pin,
>+					      int *net_if_idx)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+
>+	if (!pf->vsi[0] || pf->vsi[0]->netdev)
>+		return -EAGAIN;
>+	*net_if_idx = pf->vsi[0]->netdev->ifindex;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_pin_select - select a recovered clock pin as a valid source
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ *
>+ * dpll subsystem callback, selects a pin for clock recovery,
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_rclk_pin_select(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	u32 freq;
>+	int ret;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (!p) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	if (!(p->flags & ICE_DPLL_RCLK_SOURCE_FLAG_EN)) {
>+		ret = -EPERM;
>+		goto unlock;
>+	}
>+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, p->idx, true, &freq);
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>+		__func__, dpll, pin, pf, ret);
>+
>+	return ret;
>+}
>+
>+static struct dpll_pin_ops ice_dpll_rclk_ops = {
>+	.mode_enable = ice_dpll_rclk_mode_enable,
>+	.mode_active = ice_dpll_rclk_mode_active,
>+	.mode_supported = ice_dpll_rclk_mode_supported,
>+	.signal_type_get = ice_dpll_rclk_pin_sig_type_get,
>+	.signal_type_supported = ice_dpll_rclk_signal_type_supported,
>+	.net_if_idx_get = ice_dpll_rclk_pin_net_if_index_get,
>+	.select = ice_dpll_rclk_pin_select,
>+};
>+
>+static struct dpll_pin_ops ice_dpll_source_ops = {
>+	.signal_type_get = ice_dpll_source_signal_type_get,
>+	.signal_type_set = ice_dpll_source_signal_type_set,
>+	.signal_type_supported = ice_dpll_source_signal_type_supported,
>+	.mode_active = ice_dpll_source_mode_active,
>+	.mode_enable = ice_dpll_source_mode_enable,
>+	.mode_supported = ice_dpll_source_mode_supported,
>+	.prio_get = ice_dpll_source_prio_get,
>+	.prio_set = ice_dpll_source_prio_set,
>+};
>+
>+static struct dpll_pin_ops ice_dpll_output_ops = {
>+	.signal_type_get = ice_dpll_output_signal_type_get,
>+	.signal_type_set = ice_dpll_output_signal_type_set,
>+	.signal_type_supported = ice_dpll_output_signal_type_supported,
>+	.mode_active = ice_dpll_output_mode_active,
>+	.mode_enable = ice_dpll_output_mode_enable,
>+	.mode_supported = ice_dpll_output_mode_supported,
>+};
>+
>+static struct dpll_device_ops ice_dpll_ops = {
>+	.lock_status_get = ice_dpll_lock_status_get,
>+	.source_pin_idx_get = ice_dpll_source_idx_get,
>+	.mode_get = ice_dpll_mode_get,
>+	.mode_supported = ice_dpll_mode_supported,
>+};
>+
>+/**
>+ * ice_dpll_release_info - release memory allocated for pins
>+ * @pf: board private structure
>+ *
>+ * Release memory allocated for pins by ice_dpll_init_info function.
>+ */
>+static void ice_dpll_release_info(struct ice_pf *pf)
>+{
>+	kfree(pf->dplls.inputs);
>+	pf->dplls.inputs = NULL;
>+	kfree(pf->dplls.outputs);
>+	pf->dplls.outputs = NULL;
>+	kfree(pf->dplls.eec.input_prio);
>+	pf->dplls.eec.input_prio = NULL;
>+	kfree(pf->dplls.pps.input_prio);
>+	pf->dplls.pps.input_prio = NULL;
>+}
>+
>+/**
>+ * ice_dpll_init_pins - initializes source or output pins information
>+ * @pf: Board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Init information about input or output pins, cache them in pins struct.
>+ */
>+static int ice_dpll_init_pins(struct ice_pf *pf,
>+			      const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>+	int ret = -EINVAL, num_pins, i;
>+	struct ice_hw *hw = &pf->hw;
>+	struct ice_dpll_pin *pins;
>+	bool input;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		input = true;
>+		pins = pf->dplls.inputs;
>+		num_pins = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		input = false;
>+		pins = pf->dplls.outputs;
>+		num_pins = pf->dplls.num_outputs;
>+	} else {
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < num_pins; i++) {
>+		pins[i].idx = i;
>+		pins[i].name = ice_cgu_get_pin_name(hw, i, input);
>+		pins[i].type = ice_cgu_get_pin_type(hw, i, input);
>+		set_bit(DPLL_PIN_MODE_CONNECTED,
>+			&pins[i].mode_supported_mask);
>+		set_bit(DPLL_PIN_MODE_DISCONNECTED,
>+			&pins[i].mode_supported_mask);
>+		if (input) {
>+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>+						      &de->input_prio[i]);
>+			if (ret)
>+				return ret;
>+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>+						      &dp->input_prio[i]);
>+			if (ret)
>+				return ret;
>+			set_bit(DPLL_PIN_MODE_SOURCE,
>+				&pins[i].mode_supported_mask);
>+		} else {
>+			set_bit(DPLL_PIN_MODE_OUTPUT,
>+				&pins[i].mode_supported_mask);
>+		}
>+		pins[i].signal_type_mask =
>+				ice_cgu_get_pin_sig_type_mask(hw, i, input);
>+		ret = ice_dpll_pin_update(hw, &pins[i], pin_type);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
>+ * @dpll_eec: dpll_eec dpll pointer
>+ * @dpll_pps: dpll_pps dpll pointer
>+ * @pins: pointer to pins array
>+ * @count: number of pins
>+ *
>+ * Deregister and free pins of a given array of pins from dpll devices registered
>+ * in dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * positive - number of errors encounterd on pin's deregistration.
>+ */
>+static int
>+ice_dpll_release_pins(struct dpll_device *dpll_eec,
>+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
>+		      int count)
>+{
>+	int i, ret, err;
>+
>+	for (i = 0; i < count; i++) {
>+		struct ice_dpll_pin *p = &pins[i];
>+
>+		if (p && p->pin) {
>+			if (dpll_eec) {
>+				ret = dpll_pin_deregister(dpll_eec, p->pin);
>+				if (ret)
>+					err++;
>+			}
>+			if (dpll_pps) {
>+				ret = dpll_pin_deregister(dpll_pps, p->pin);
>+				if (ret)
>+					err++;
>+			}
>+			dpll_pin_free(p->pin);
>+			p->pin = NULL;
>+		}
>+	}
>+
>+	return err;
>+}
>+
>+/**
>+ * ice_dpll_register_pins - register pins with a dpll
>+ * @pf: board private structure
>+ * @dpll: registered dpll pointer
>+ * @pin_type: type of pins being registered
>+ *
>+ * Register source or output pins within given DPLL in a Linux dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_register_pins(struct ice_pf *pf, struct dpll_device *dpll,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *pins;
>+	struct dpll_pin_ops *ops;
>+	int ret, i, count;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		ops = &ice_dpll_source_ops;
>+		pins = pf->dplls.inputs;
>+		count = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		ops = &ice_dpll_output_ops;
>+		pins = pf->dplls.outputs;
>+		count = pf->dplls.num_outputs;
>+	} else {
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < count; i++) {
>+		pins[i].pin = dpll_pin_alloc(pins[i].name, pins[i].type);
>+		if (IS_ERR_OR_NULL(pins[i].pin))
>+			return -ENOMEM;
>+
>+		ret = dpll_pin_register(dpll, pins[i].pin, ops, pf);
>+		if (ret)
>+			return -ENOSPC;
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_register_shared_pins - register shared pins in DPLL subsystem
>+ * @pf: board private structure
>+ * @dpll_o: registered dpll pointer (owner)
>+ * @dpll: registered dpll pointer
>+ * @type: type of pins being registered
>+ *
>+ * Register pins from given owner dpll within given dpll in Linux dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_register_shared_pins(struct ice_pf *pf, struct dpll_device *dpll_o,
>+			      struct dpll_device *dpll,
>+			      const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *pins;
>+	struct dpll_pin_ops *ops;
>+	int ret, i, count;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		ops = &ice_dpll_source_ops;
>+		pins = pf->dplls.inputs;
>+		count = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		ops = &ice_dpll_output_ops;
>+		pins = pf->dplls.outputs;
>+		count = pf->dplls.num_outputs;
>+	} else {
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < count; i++) {
>+		ret = dpll_shared_pin_register(dpll_o, dpll, pins[i].name,
>+					       ops, pf);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_init_info - prepare pf's dpll information structure
>+ * @pf: board private structure
>+ *
>+ * Acquire (from HW) and set basic dpll information (on pf->dplls struct).
>+ *
>+ * Return:
>+ *  0 - success
>+ *  negative - error
>+ */
>+static int ice_dpll_init_info(struct ice_pf *pf)
>+{
>+	struct ice_aqc_get_cgu_abilities abilities;
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_hw *hw = &pf->hw;
>+	int ret, alloc_size;
>+
>+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>+	if (ret) {
>+		dev_err(ice_pf_to_dev(pf),
>+			"err:%d %s failed to read cgu abilities\n",
>+			ret, ice_aq_str(hw->adminq.sq_last_status));
>+		return ret;
>+	}
>+
>+	de->dpll_idx = abilities.eec_dpll_idx;
>+	dp->dpll_idx = abilities.pps_dpll_idx;
>+	d->num_inputs = abilities.num_inputs;
>+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!d->inputs)
>+		return -ENOMEM;
>+
>+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!de->input_prio)
>+		return -ENOMEM;
>+
>+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!dp->input_prio)
>+		return -ENOMEM;
>+
>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (ret)
>+		goto release_info;
>+
>+	d->num_outputs = abilities.num_outputs;
>+	alloc_size = sizeof(*d->outputs) * d->num_outputs;
>+	d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!d->outputs)
>+		goto release_info;
>+
>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>+	if (ret)
>+		goto release_info;
>+
>+	dev_dbg(ice_pf_to_dev(pf), "%s - success, inputs:%u, outputs:%u\n", __func__,
>+		abilities.num_inputs, abilities.num_outputs);
>+
>+	return 0;
>+
>+release_info:
>+	dev_err(ice_pf_to_dev(pf),
>+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
>+		__func__, d->inputs, de->input_prio,
>+		dp->input_prio, d->outputs);
>+	ice_dpll_release_info(pf);
>+	return ret;
>+}
>+
>+/**
>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>+ * @pf: board private structure
>+ * @clock_id: holds generated clock_id
>+ *
>+ * Generates unique (per board) clock_id for allocation and search of dpll
>+ * devices in Linux dpll subsystem.
>+ */
>+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
>+{
>+	*clock_id = pci_get_dsn(pf->pdev);
>+}
>+
>+/**
>+ * ice_dpll_init_dpll
>+ * @pf: board private structure
>+ *
>+ * Allocate and register dpll in dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - allocation fails
>+ */
>+static int ice_dpll_init_dpll(struct ice_pf *pf)
>+{
>+	struct device *dev = ice_pf_to_dev(pf);
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	u64 clock_id = 0;
>+	int ret = 0;
>+
>+	ice_generate_clock_id(pf, &clock_id);
>+
>+	de->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_EEC,
>+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
>+	if (!de->dpll) {
>+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (eec)\n");
>+		return -ENOMEM;
>+	}
>+
>+	dp->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_PPS,
>+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
>+	if (!dp->dpll) {
>+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (pps)\n");
>+		return -ENOMEM;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_update_state
>+ * @hw: board private structure
>+ * @d: pointer to queried dpll device
>+ *
>+ * Poll current state of dpll from hw and update ice_dpll struct.
>+ * Return:
>+ * * 0 - success
>+ * * negative - AQ failure
>+ */
>+static int ice_dpll_update_state(struct ice_hw *hw, struct ice_dpll *d)
>+{
>+	int ret;
>+
>+	ret = ice_get_cgu_state(hw, d->dpll_idx, d->prev_dpll_state,
>+				&d->source_idx, &d->ref_state, &d->eec_mode,
>+				&d->phase_offset, &d->dpll_state);
>+
>+	dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+		"update dpll=%d, src_idx:%u, state:%d, prev:%d\n",
>+		d->dpll_idx, d->source_idx,
>+		d->dpll_state, d->prev_dpll_state);
>+
>+	if (ret)
>+		dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+			"update dpll=%d state failed, ret=%d %s\n",
>+			d->dpll_idx, ret,
>+			ice_aq_str(hw->adminq.sq_last_status));
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_notify_changes - notify dpll subsystem about changes
>+ * @d: pointer do dpll
>+ *
>+ * Once change detected appropriate event is submitted to the dpll subsystem.
>+ */
>+static void ice_dpll_notify_changes(struct ice_dpll *d)
>+{
>+	if (d->prev_dpll_state != d->dpll_state) {
>+		d->prev_dpll_state = d->dpll_state;
>+		dpll_device_notify(d->dpll, DPLL_CHANGE_LOCK_STATUS);
>+	}
>+	if (d->prev_source_idx != d->source_idx) {
>+		d->prev_source_idx = d->source_idx;
>+		dpll_device_notify(d->dpll, DPLL_CHANGE_SOURCE_PIN);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_periodic_work - DPLLs periodic worker
>+ * @work: pointer to kthread_work structure
>+ *
>+ * DPLLs periodic worker is responsible for polling state of dpll.
>+ */
>+static void ice_dpll_periodic_work(struct kthread_work *work)
>+{
>+	struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	int ret = 0;
>+
>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))

Why do you need to check the flag there, this would should not be
ever scheduled in case the flag was not set.


>+		return;
>+	mutex_lock(&d->lock);
>+	ret = ice_dpll_update_state(&pf->hw, de);
>+	if (!ret)
>+		ret = ice_dpll_update_state(&pf->hw, dp);
>+	if (ret) {
>+		d->cgu_state_acq_err_num++;
>+		/* stop rescheduling this worker */
>+		if (d->cgu_state_acq_err_num >
>+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
>+			dev_err(ice_pf_to_dev(pf),
>+				"EEC/PPS DPLLs periodic work disabled\n");
>+			return;
>+		}
>+	}
>+	mutex_unlock(&d->lock);
>+	ice_dpll_notify_changes(de);
>+	ice_dpll_notify_changes(dp);
>+
>+	/* Run twice a second or reschedule if update failed */
>+	kthread_queue_delayed_work(d->kworker, &d->work,
>+				   ret ? msecs_to_jiffies(10) :
>+				   msecs_to_jiffies(500));
>+}
>+
>+/**
>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>+ * @pf: board private structure
>+ *
>+ * Create and start DPLLs periodic worker.
>+ * Return:
>+ * * 0 - success
>+ * * negative - create worker failure
>+ */
>+static int ice_dpll_init_worker(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	struct kthread_worker *kworker;
>+
>+	ice_dpll_update_state(&pf->hw, &d->eec);
>+	ice_dpll_update_state(&pf->hw, &d->pps);
>+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>+	kworker = kthread_create_worker(0, "ice-dplls-%s",
>+					dev_name(ice_pf_to_dev(pf)));
>+	if (IS_ERR(kworker))
>+		return PTR_ERR(kworker);
>+	d->kworker = kworker;
>+	d->cgu_state_acq_err_num = 0;
>+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
>+
>+	return 0;
>+}
>+
>+/**
>+ * __ice_dpll_release - Disable the driver/HW support for DPLL and unregister
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * This function handles the cleanup work required from the initialization by
>+ * freeing resources and unregistering the dpll.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void __ice_dpll_release(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_dpll *de = &d->eec;
>+	struct ice_dpll *dp = &d->pps;
>+	int ret;
>+
>+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
>+				    d->num_inputs);
>+	if (ret)
>+		dev_warn(ice_pf_to_dev(pf),
>+			 "pin deregister on PPS dpll err=%d\n", ret);
>+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
>+				    d->num_outputs);
>+	if (ret)
>+		dev_warn(ice_pf_to_dev(pf),
>+			 "pin deregister on PPS dpll err=%d\n", ret);
>+	ice_dpll_release_info(pf);
>+	if (dp->dpll) {
>+		dpll_device_unregister(dp->dpll);
>+		dpll_device_free(dp->dpll);
>+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
>+	}
>+
>+	if (de->dpll) {
>+		dpll_device_unregister(de->dpll);
>+		dpll_device_free(de->dpll);
>+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
>+	}
>+
>+	kthread_cancel_delayed_work_sync(&d->work);
>+	if (d->kworker) {
>+		kthread_destroy_worker(d->kworker);
>+		d->kworker = NULL;
>+		dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>+	}
>+}
>+
>+/**
>+ * ice_dpll_init - Initialize DPLLs support
>+ * @pf: board private structure
>+ *
>+ * Set up the device as owner of DPLLs registering them and pins connected
>+ * within Linux dpll subsystem. Allow userpsace to obtain state of DPLL
>+ * and handling of DPLL configuration requests.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+int ice_dpll_init(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	int err;
>+
>+	mutex_init(&d->lock);
>+	mutex_lock(&d->lock);

It is always odd to see the lock being created and locked right away.
Why do you need to lock it here?


>+	err = ice_dpll_init_info(pf);
>+	if (err)
>+		goto unlock;
>+	err = ice_dpll_init_dpll(pf);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_pins(pf, d->eec.dpll, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_pins(pf, d->eec.dpll, ICE_DPLL_PIN_TYPE_OUTPUT);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>+	if (err)
>+		goto release;
>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>+	err = ice_dpll_init_worker(pf);
>+	if (err)
>+		goto release;
>+	mutex_unlock(&d->lock);
>+	dev_dbg(ice_pf_to_dev(pf), "DPLLs init successful\n");
>+
>+	return err;
>+release:
>+	__ice_dpll_release(pf);
>+unlock:
>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>+	mutex_unlock(&d->lock);
>+	mutex_destroy(&d->lock);
>+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure\n");
>+
>+	return err;
>+}
>+
>+/**
>+ * ice_dpll_release - Disable the driver/HW support for DPLLs and unregister
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * This function handles the cleanup work required from the initialization by
>+ * freeing resources and unregistering the dpll.
>+ */
>+void ice_dpll_release(struct ice_pf *pf)
>+{
>+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>+		mutex_lock(&pf->dplls.lock);
>+		clear_bit(ICE_FLAG_DPLL, pf->flags);
>+		__ice_dpll_release(pf);
>+		mutex_unlock(&pf->dplls.lock);
>+		mutex_destroy(&pf->dplls.lock);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_rclk_pin_init - init the pin info for recovered clock
>+ * @attr: structure with pin attributes
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+void ice_dpll_rclk_pin_init(struct ice_dpll_pin *p)
>+{
>+	p->flags = ICE_DPLL_RCLK_SOURCE_FLAG_EN;
>+	p->type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>+	set_bit(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ, &p->signal_type_mask);
>+	set_bit(DPLL_PIN_MODE_CONNECTED,	  &p->mode_supported_mask);
>+	set_bit(DPLL_PIN_MODE_DISCONNECTED,	  &p->mode_supported_mask);
>+	set_bit(DPLL_PIN_MODE_SOURCE,		  &p->mode_supported_mask);
>+	ice_dpll_pin_update(0, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * __ice_dpll_rclk_release - unregister the recovered pin for dpll device
>+ * @pf: board private structure
>+ *
>+ * This function handles the cleanup work required from the initialization by
>+ * freeing resources and unregistering the recovered pin.
>+ */
>+void __ice_dpll_rclk_release(struct ice_pf *pf)
>+{
>+	int ret = 0;
>+
>+	if (pf->dplls.eec.dpll) {
>+		if (pf->dplls.rclk[0].pin)
>+			ret = dpll_pin_deregister(pf->dplls.eec.dpll,
>+						  pf->dplls.rclk[0].pin);
>+		dpll_pin_free(pf->dplls.rclk->pin);
>+		kfree(pf->dplls.rclk);
>+	}
>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
>+}
>+
>+/**
>+ * ice_dpll_rclk_pins_init - init the pin for recovered clock
>+ * @pf: board private structure
>+ * @first_parent: pointer to a first parent pin
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+int ice_dpll_rclk_pins_init(struct ice_pf *pf, struct ice_dpll_pin *first_parent)
>+{
>+	struct ice_dpll_pin *parent, *p;
>+	char *name;
>+	int i, ret;
>+
>+	if (pf->dplls.rclk)
>+		return -EEXIST;
>+	pf->dplls.rclk = kcalloc(pf->dplls.num_rclk, sizeof(*pf->dplls.rclk),
>+				 GFP_KERNEL);
>+	if (!pf->dplls.rclk)
>+		goto release;
>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>+		p = &pf->dplls.rclk[i];
>+		if (!p)
>+			goto release;
>+		ice_dpll_rclk_pin_init(p);
>+		parent = first_parent + i;
>+		if (!parent)
>+			goto release;
>+		p->idx = i;
>+		name = kcalloc(DPLL_PIN_DESC_LEN, sizeof(*p->name), GFP_KERNEL);
>+		if (!name)
>+			goto release;
>+		snprintf(name, DPLL_PIN_DESC_LEN - 1, "%s-%u",
>+			 parent->name, pf->hw.pf_id);
>+		p->name = name;
>+		p->pin = dpll_pin_alloc(p->name, p->type);
>+		if (IS_ERR_OR_NULL(p->pin))
>+			goto release;
>+		ret = dpll_muxed_pin_register(pf->dplls.eec.dpll, parent->name,
>+					      p->pin, &ice_dpll_rclk_ops, pf);
>+		if (ret)
>+			goto release;
>+		ret = dpll_shared_pin_register(pf->dplls.eec.dpll,
>+					       pf->dplls.pps.dpll,
>+					       p->name,
>+					       &ice_dpll_rclk_ops, pf);
>+		if (ret)
>+			goto release;
>+	}
>+
>+	return ret;
>+release:
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s releasing - p: %p, parent:%p, p->pin:%p name:%s, ret:%d\n",
>+		__func__, p, parent, p->pin, name, ret);
>+	__ice_dpll_rclk_release(pf);
>+	return -ENOMEM;
>+}
>+
>+/**
>+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
>+ * @pf: board private structure
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf)
>+{
>+	u64 clock_id = 0;
>+
>+	ice_generate_clock_id(pf, &clock_id);
>+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,

I have to say I'm a bit lost in this code. Why exactly do you need this
here? Looks like the pointer was set in ice_dpll_init_dpll().

Or, is that in case of a different PF instantiating the DPLL instances?
If yes, I'm pretty sure what it is wrong. What is the PF which did
instanticate those unbinds? You have to share the dpll instance,
refcount it.

Btw, you have a problem during init as well, as the order matters. What
if the other function probes only after executing this? You got -EFAULT
here and bail out.

In mlx5, I also share one dpll instance between 2 PFs. What I do is I
create mlx5-dpll instance which is refcounted, created by first probed
PF and removed by the last one. In mlx5 case, the PFs are equal, nobody
is an owner of the dpll. In your case, I think it is different. So
probably better to implement the logic in driver then in the dpll core.

Then you don't need dpll_device_get_by_clock_id at all. If you decide to
implement that in dpll core, I believe that there should be some
functions like:
dpll = dpll_device_get(ops, clock_id, ...)  - to create/get reference
dpll_device_put(dpll)                       - to put reference/destroy

First caller of dpll_device_get() actually makes dpll to instantiate the
device.



>+							 DPLL_TYPE_EEC, 0);
>+	if (!pf->dplls.eec.dpll)
>+		return -EFAULT;
>+	pf->dplls.pps.dpll = dpll_device_get_by_clock_id(clock_id,
>+							 DPLL_TYPE_PPS, 0);
>+	if (!pf->dplls.pps.dpll)
>+		return -EFAULT;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_parent_pins_init - initialize the recovered clock parent pins
>+ * @pf: board private structure
>+ * @base_rclk_idx: number of first recovered clock pin in DPLL
>+ *
>+ * This function shall be executed only if ICE_FLAG_DPLL feature is not

Feature? It's a flag.


>+ * supported.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+static int ice_dpll_rclk_parent_pins_init(struct ice_pf *pf, u8 base_rclk_idx)
>+{
>+	int i;
>+
>+	if (pf->dplls.inputs)
>+		return -EINVAL;
>+	pf->dplls.inputs = kcalloc(pf->dplls.num_rclk,
>+				   sizeof(*pf->dplls.inputs), GFP_KERNEL);
>+
>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>+		const char *desc;
>+
>+		desc = ice_cgu_get_pin_name(&pf->hw, base_rclk_idx + i, true);
>+		if (!desc)
>+			return -EINVAL;
>+		pf->dplls.inputs[i].name = desc;
>+	}
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_init - Enable support for DPLL's PHY clock recovery
>+ * @pf: board private structure
>+ *
>+ * Context:
>+ * Acquires a pf->dplls.lock. If PF is not an owner of DPLL it shall find and
>+ * connect its pins with the device dpll.
>+ *
>+ * This function handles enablement of PHY clock recovery part for timesync
>+ * capabilities.
>+ * Prepares and initalizes resources required to register its PHY clock sources
>+ * within DPLL subsystem.
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+int ice_dpll_rclk_init(struct ice_pf *pf)
>+{
>+	struct ice_dpll_pin *first_parent = NULL;
>+	u8 base_rclk_idx;
>+	int ret;
>+
>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &base_rclk_idx,
>+					&pf->dplls.num_rclk);
>+	if (ret)
>+		return ret;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>+		ret = ice_dpll_rclk_find_dplls(pf);
>+		dev_dbg(ice_pf_to_dev(pf), "ecc:%p, pps:%p\n",
>+			pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>+		if (ret)
>+			goto unlock;
>+		ret = ice_dpll_rclk_parent_pins_init(pf, base_rclk_idx);
>+		if (ret)
>+			goto unlock;
>+		first_parent = &pf->dplls.inputs[0];
>+	} else {
>+		first_parent = &pf->dplls.inputs[base_rclk_idx];
>+	}
>+	if (!first_parent) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	ret = ice_dpll_rclk_pins_init(pf, first_parent);
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK init ret=%d\n", ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_release - Disable the support for DPLL's PHY clock recovery
>+ * @pf: board private structure
>+ *
>+ * Context:
>+ * Acquires a pf->dplls.lock. Requires dplls to be present, must be called
>+ * before dplls are realesed.
>+ *
>+ * This function handles the cleanup work of resources allocated for enablement
>+ * of PHY recovery clock mechanics.
>+ * Unregisters RCLK pins and frees pin's memory allocated by ice_dpll_rclk_init.
>+ */
>+void ice_dpll_rclk_release(struct ice_pf *pf)
>+{
>+	int i, ret = 0;
>+
>+	if (!pf->dplls.rclk)
>+		return;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>+		if (pf->dplls.rclk[i].pin) {
>+			dpll_pin_deregister(pf->dplls.eec.dpll,
>+					    pf->dplls.rclk[i].pin);
>+			dpll_pin_deregister(pf->dplls.pps.dpll,
>+					    pf->dplls.rclk[i].pin);
>+			dpll_pin_free(pf->dplls.rclk[i].pin);
>+			pf->dplls.rclk[i].pin = NULL;
>+		}
>+		kfree(pf->dplls.rclk[i].name);
>+		pf->dplls.rclk[i].name = NULL;
>+	}
>+	/* inputs were prepared only for RCLK, release them here */
>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>+		kfree(pf->dplls.inputs);
>+		pf->dplls.inputs = NULL;
>+	}
>+	kfree(pf->dplls.rclk);
>+	pf->dplls.rclk = NULL;
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
>+}
>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
>new file mode 100644
>index 000000000000..3390d60f2fab
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>@@ -0,0 +1,99 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#ifndef _ICE_DPLL_H_
>+#define _ICE_DPLL_H_
>+
>+#include "ice.h"
>+
>+#define ICE_DPLL_PRIO_MAX	0xF
>+
>+/** ice_dpll_pin - store info about pins
>+ * @pin: dpll pin structure
>+ * @flags: pin flags returned from HW
>+ * @idx: ice pin private idx
>+ * @type: type of a pin
>+ * @signal_type: current signal type
>+ * @signal_type_mask: signal types supported
>+ * @freq: current frequency of a pin
>+ * @mode_mask: current pin modes as bitmask
>+ * @mode_supported_mask: supported pin modes
>+ * @name: pin name
>+ */
>+struct ice_dpll_pin {
>+	struct dpll_pin *pin;
>+#define ICE_DPLL_RCLK_SOURCE_FLAG_EN	BIT(0)
>+	u8 flags;
>+	u8 idx;
>+	enum dpll_pin_type type;
>+	enum dpll_pin_signal_type signal_type;
>+	unsigned long signal_type_mask;
>+	u32 freq;
>+	unsigned long mode_mask;
>+	unsigned long mode_supported_mask;
>+	const char *name;
>+};
>+
>+/** ice_dpll - store info required for DPLL control
>+ * @dpll: pointer to dpll dev
>+ * @dpll_idx: index of dpll on the NIC
>+ * @source_idx: source currently selected
>+ * @prev_source_idx: source previously selected
>+ * @ref_state: state of dpll reference signals
>+ * @eec_mode: eec_mode dpll is configured for
>+ * @phase_offset: phase delay of a dpll
>+ * @input_prio: priorities of each input
>+ * @dpll_state: current dpll sync state
>+ * @prev_dpll_state: last dpll sync state
>+ */
>+struct ice_dpll {
>+	struct dpll_device *dpll;
>+	int dpll_idx;
>+	u8 source_idx;
>+	u8 prev_source_idx;
>+	u8 ref_state;
>+	u8 eec_mode;
>+	s64 phase_offset;
>+	u8 *input_prio;
>+	enum ice_cgu_state dpll_state;
>+	enum ice_cgu_state prev_dpll_state;
>+};
>+
>+/** ice_dplls - store info required for CCU (clock controlling unit)
>+ * @kworker: periodic worker
>+ * @work: periodic work
>+ * @lock: locks access to configuration of a dpll
>+ * @eec: pointer to EEC dpll dev
>+ * @pps: pointer to PPS dpll dev
>+ * @inputs: input pins pointer
>+ * @outputs: output pins pointer
>+ * @rclk: recovered pins pointer
>+ * @num_inputs: number of input pins available on dpll
>+ * @num_outputs: number of output pins available on dpll
>+ * @num_rclk: number of recovered clock pins available on dpll
>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>+ */
>+struct ice_dplls {
>+	struct kthread_worker *kworker;
>+	struct kthread_delayed_work work;
>+	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 *rclk;
>+	u32 num_inputs;
>+	u32 num_outputs;
>+	u8 num_rclk;
>+	int cgu_state_acq_err_num;
>+};
>+
>+int ice_dpll_init(struct ice_pf *pf);
>+
>+void ice_dpll_release(struct ice_pf *pf);
>+
>+int ice_dpll_rclk_init(struct ice_pf *pf);
>+
>+void ice_dpll_rclk_release(struct ice_pf *pf);
>+
>+#endif
>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
>index a9a7f8b52140..8b65f4ad245e 100644
>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>@@ -4896,6 +4896,12 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
> 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
> 		ice_ptp_init(pf);
> 
>+	if (ice_is_feature_supported(pf, ICE_F_CGU))
>+		ice_dpll_init(pf);
>+
>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>+		ice_dpll_rclk_init(pf);
>+
> 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
> 		ice_gnss_init(pf);
> 
>@@ -5078,6 +5084,10 @@ static void ice_remove(struct pci_dev *pdev)
> 		ice_ptp_release(pf);
> 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
> 		ice_gnss_exit(pf);
>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>+		ice_dpll_rclk_release(pf);
>+	if (ice_is_feature_supported(pf, ICE_F_CGU))
>+		ice_dpll_release(pf);
> 	if (!ice_is_safe_mode(pf))
> 		ice_remove_arfs(pf);
> 	ice_setup_mc_magic_wake(pf);
>-- 
>2.30.2
>

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

* Re: [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
@ 2023-01-19 14:54     ` Jiri Pirko
  0 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-19 14:54 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk, Milena Olech,
	Michal Michalik

Tue, Jan 17, 2023 at 07:00:51PM CET, vadfed@meta.com wrote:
>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>Control over clock generation unit is required for further development
>of Synchronous Ethernet feature. Interface provides ability to obtain
>current state of a dpll, its sources and outputs which are pins, and
>allows their configuration.
>
>Co-developed-by: Milena Olech <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
>---
> drivers/net/ethernet/intel/Kconfig        |    1 +
> drivers/net/ethernet/intel/ice/Makefile   |    3 +-
> drivers/net/ethernet/intel/ice/ice.h      |    4 +
> drivers/net/ethernet/intel/ice/ice_dpll.c | 2115 +++++++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h |   99 +
> drivers/net/ethernet/intel/ice/ice_main.c |   10 +
> 6 files changed, 2231 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
>
>diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
>index 3facb55b7161..dcf0e12991bf 100644
>--- a/drivers/net/ethernet/intel/Kconfig
>+++ b/drivers/net/ethernet/intel/Kconfig
>@@ -300,6 +300,7 @@ config ICE
> 	select DIMLIB
> 	select NET_DEVLINK
> 	select PLDMFW
>+	select DPLL
> 	help
> 	  This driver supports Intel(R) Ethernet Connection E800 Series of
> 	  devices.  For more information on how to identify your adapter, go
>diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
>index 9183d480b70b..ebfd456f76cd 100644
>--- a/drivers/net/ethernet/intel/ice/Makefile
>+++ b/drivers/net/ethernet/intel/ice/Makefile
>@@ -32,7 +32,8 @@ ice-y := ice_main.o	\
> 	 ice_lag.o	\
> 	 ice_ethtool.o  \
> 	 ice_repr.o	\
>-	 ice_tc_lib.o
>+	 ice_tc_lib.o	\
>+	 ice_dpll.o
> ice-$(CONFIG_PCI_IOV) +=	\
> 	ice_sriov.o		\
> 	ice_virtchnl.o		\
>diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
>index 3368dba42789..efc1844ef77c 100644
>--- a/drivers/net/ethernet/intel/ice/ice.h
>+++ b/drivers/net/ethernet/intel/ice/ice.h
>@@ -73,6 +73,7 @@
> #include "ice_lag.h"
> #include "ice_vsi_vlan_ops.h"
> #include "ice_gnss.h"
>+#include "ice_dpll.h"
> 
> #define ICE_BAR0		0
> #define ICE_REQ_DESC_MULTIPLE	32
>@@ -198,6 +199,7 @@
> enum ice_feature {
> 	ICE_F_DSCP,
> 	ICE_F_PTP_EXTTS,
>+	ICE_F_PHY_RCLK,
> 	ICE_F_SMA_CTRL,
> 	ICE_F_CGU,
> 	ICE_F_GNSS,
>@@ -509,6 +511,7 @@ enum ice_pf_flags {
> 	ICE_FLAG_PLUG_AUX_DEV,
> 	ICE_FLAG_MTU_CHANGED,
> 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
>+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
> 	ICE_PF_FLAGS_NBITS		/* must be last */
> };
> 
>@@ -633,6 +636,7 @@ struct ice_pf {
> #define ICE_VF_AGG_NODE_ID_START	65
> #define ICE_MAX_VF_AGG_NODES		32
> 	struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
>+	struct ice_dplls dplls;
> };
> 
> struct ice_netdev_priv {
>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
>new file mode 100644
>index 000000000000..6ed1fcee4e03
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>@@ -0,0 +1,2115 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#include "ice.h"
>+#include "ice_lib.h"
>+#include "ice_trace.h"
>+#include <linux/dpll.h>
>+#include <uapi/linux/dpll.h>

Don't include uapi header directly. Inlude of linux/dpll.h is enough.


>+
>+#define CGU_STATE_ACQ_ERR_THRESHOLD	50
>+#define ICE_DPLL_FREQ_1_HZ		1
>+#define ICE_DPLL_FREQ_10_MHZ		10000000
>+
>+/**
>+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock status
>+ */
>+static const enum dpll_lock_status
>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {

Don't use __ define.


>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>+};
>+
>+/**
>+ * ice_dpll_pin_type - enumerate ince pin types
>+ */
>+enum ice_dpll_pin_type {
>+	ICE_DPLL_PIN_INVALID = 0,
>+	ICE_DPLL_PIN_TYPE_SOURCE,
>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
>+};
>+
>+/**
>+ * pin_type_name - string names of ice pin types
>+ */
>+static const char * const pin_type_name[] = {
>+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
>+};
>+
>+/**
>+ * ice_dpll_pin_signal_type_to_freq - translate pin_signal_type to freq value
>+ * @sig_type: signal type to find frequency
>+ * @freq: on success - frequency of a signal type
>+ *
>+ * Return:
>+ * * 0 - frequency valid
>+ * * negative - error
>+ */
>+static inline int
>+ice_dpll_pin_signal_type_to_freq(const enum dpll_pin_signal_type sig_type,
>+				 u32 *freq)
>+{
>+	if (sig_type == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>+		return -EINVAL;
>+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_1_PPS)
>+		*freq = ICE_DPLL_FREQ_1_HZ;
>+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_10_MHZ)
>+		*freq = ICE_DPLL_FREQ_10_MHZ;
>+	else
>+		return -EINVAL;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_pin_freq_to_signal_type - translate pin freq to signal type
>+ * @freq: frequency to translate
>+ *
>+ * Return: signal type of a pin based on frequency.
>+ */
>+static inline enum dpll_pin_signal_type
>+ice_dpll_pin_freq_to_signal_type(u32 freq)
>+{
>+	if (freq == ICE_DPLL_FREQ_1_HZ)
>+		return DPLL_PIN_SIGNAL_TYPE_1_PPS;
>+	else if (freq == ICE_DPLL_FREQ_10_MHZ)
>+		return DPLL_PIN_SIGNAL_TYPE_10_MHZ;
>+	else
>+		return DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ;
>+}
>+
>+/**
>+ * ice_dpll_pin_signal_type_set - set pin's signal type in hardware
>+ * @pf: Board private structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being configured
>+ * @sig_type: signal type to be set
>+ *
>+ * Translate pin signal type to frequency and set it on a pin.
>+ *
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+__ice_dpll_pin_signal_type_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+			       const enum ice_dpll_pin_type pin_type,
>+			       const enum dpll_pin_signal_type sig_type)
>+{
>+	u8 flags;
>+	u32 freq;
>+	int ret;
>+
>+	ret = ice_dpll_pin_signal_type_to_freq(sig_type, &freq);
>+	if (ret)
>+		return ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>+					       pin->flags, freq, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags = pin->flags | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>+						0, freq, 0);
>+	} else {
>+		ret = -EINVAL;
>+	}
>+
>+	if (ret) {
>+		dev_dbg(ice_pf_to_dev(pf),
>+			"err:%d %s failed to set pin freq:%u on pin:%u\n",
>+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>+			freq, pin->idx);
>+	} else {
>+		pin->signal_type = sig_type;
>+		pin->freq = freq;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_enable - enable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being enabled
>+ *
>+ * Enable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    const enum ice_dpll_pin_type pin_type)
>+{
>+	u8 flags = pin->flags;
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		flags |= ICE_DPLL_RCLK_SOURCE_FLAG_EN;
>+		ret = 0;
>+	}
>+	if (ret)
>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+			"err:%d %s failed to enable %s pin:%u\n",
>+			ret, ice_aq_str(hw->adminq.sq_last_status),
>+			pin_type_name[pin_type], pin->idx);
>+	else
>+		pin->flags = flags;
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_disable - disable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being disabled
>+ *
>+ * Disable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		     enum ice_dpll_pin_type pin_type)
>+{
>+	u8 flags = pin->flags;
>+	int ret;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		flags &= ~(ICE_DPLL_RCLK_SOURCE_FLAG_EN);
>+		ret = ice_aq_set_phy_rec_clk_out(hw, pin->idx, false,
>+						 &pin->freq);
>+	}
>+
>+	if (ret)
>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+			"err:%d %s failed to disable %s pin:%u\n",
>+			ret, ice_aq_str(hw->adminq.sq_last_status),
>+			pin_type_name[pin_type], pin->idx);
>+	else
>+		pin->flags = flags;
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_update - update pin's mode
>+ * @hw: private board struct
>+ * @pin: structure with pin attributes to be updated
>+ * @pin_type: type of pin being updated
>+ *
>+ * Determine pin current mode, frequency and signal type. Then update struct
>+ * holding the pin info.
>+ *
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+int
>+ice_dpll_pin_update(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+		    const enum ice_dpll_pin_type pin_type)
>+{
>+	int ret;
>+
>+	pin->mode_mask = 0;
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		ret = ice_aq_get_input_pin_cfg(hw, pin->idx, NULL, NULL, NULL,
>+					       &pin->flags, &pin->freq, NULL);
>+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
>+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags)
>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>+		else
>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		ret = ice_aq_get_output_pin_cfg(hw, pin->idx, &pin->flags,
>+						NULL, &pin->freq, NULL);
>+		set_bit(DPLL_PIN_MODE_OUTPUT, &pin->mode_mask);
>+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags)
>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>+		else
>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
>+		if (ICE_DPLL_RCLK_SOURCE_FLAG_EN & pin->flags)
>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>+		else
>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>+		ret = 0;
>+	}
>+	pin->signal_type = ice_dpll_pin_freq_to_signal_type(pin->freq);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_mode_set - set pin's mode
>+ * @pf: Board private structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of modified pin
>+ * @mode: requested mode
>+ *
>+ * Determine requested pin mode set it on a pin.
>+ *
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_mode_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+		      const enum ice_dpll_pin_type pin_type,
>+		      const enum dpll_pin_mode mode)
>+{
>+	int ret;
>+
>+	if (!test_bit(mode, &pin->mode_supported_mask))
>+		return -EINVAL;
>+
>+	if (test_bit(mode, &pin->mode_mask))
>+		return 0;
>+
>+	if (mode == DPLL_PIN_MODE_CONNECTED)
>+		ret = ice_dpll_pin_enable(&pf->hw, pin, pin_type);
>+	else if (mode == DPLL_PIN_MODE_DISCONNECTED)
>+		ret = ice_dpll_pin_disable(&pf->hw, pin, pin_type);
>+	else
>+		ret = -EINVAL;
>+
>+	if (!ret)
>+		ret = ice_dpll_pin_update(&pf->hw, pin, pin_type);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_find_dpll - find ice_dpll on a pf
>+ * @pf: private board structure
>+ * @dpll: kernel's dpll_device pointer to be searched
>+ *
>+ * Return:
>+ * * pointer if ice_dpll with given device dpll pointer is found
>+ * * NULL if not found
>+ */
>+static struct ice_dpll
>+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
>+{
>+	if (!pf || !dpll)
>+		return NULL;
>+
>+	return (dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
>+		dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL);

Return is not a function, don't use ()'s here.


>+}
>+
>+/**
>+ * ice_find_pin - find ice_dpll_pin on a pf
>+ * @pf: private board structure
>+ * @pin: kernel's dpll_pin pointer to be searched for
>+ * @pin_type: type of pins to be searched for
>+ *
>+ * Find and return internal ice pin info pointer holding data of given dpll subsystem
>+ * pin pointer.
>+ *
>+ * Return:
>+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer was
>+ * found in pf internal pin data.
>+ * * NULL - if pin was not found.
>+ */
>+static struct ice_dpll_pin
>+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
>+	      enum ice_dpll_pin_type pin_type)
>+
>+{
>+	struct ice_dpll_pin *pins;
>+	int pin_num, i;
>+
>+	if (!pin || !pf)
>+		return NULL;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		pins = pf->dplls.inputs;
>+		pin_num = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		pins = pf->dplls.outputs;
>+		pin_num = pf->dplls.num_outputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>+		pins = pf->dplls.rclk;
>+		pin_num = pf->dplls.num_rclk;
>+	} else {
>+		return NULL;
>+	}
>+
>+	for (i = 0; i < pin_num; i++)
>+		if (pin == pins[i].pin)
>+			return &pins[i];
>+
>+	return NULL;
>+}
>+
>+/**
>+ * ice_dpll_hw_source_prio_set - set source priority value in hardware
>+ * @pf: board private structure
>+ * @dpll: ice dpll pointer
>+ * @pin: ice pin pointer
>+ * @prio: priority value being set on a dpll
>+ *
>+ * Internal wrapper for setting the priority in the hardware.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_hw_source_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
>+			    struct ice_dpll_pin *pin, const u32 prio)
>+{
>+	int ret;
>+
>+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
>+				      (u8)prio);
>+	if (ret)
>+		dev_dbg(ice_pf_to_dev(pf),
>+			"err:%d %s failed to set pin prio:%u on pin:%u\n",
>+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>+			prio, pin->idx);
>+	else
>+		dpll->input_prio[pin->idx] = prio;
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_lock_status_get - get dpll lock status callback
>+ * @dpll: registered dpll pointer
>+ * @status: on success holds dpll's lock status
>+ *
>+ * Dpll subsystem callback, provides dpll's lock status.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_lock_status_get(const struct dpll_device *dpll,
>+				    enum dpll_lock_status *status)
>+{
>+	struct ice_pf *pf = dpll_priv(dpll);
>+	struct ice_dpll *d;
>+
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		return -EFAULT;
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll, pf);
>+	mutex_lock(&pf->dplls.lock);
>+	*status = ice_dpll_status[d->dpll_state];
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_source_idx_get - get dpll's source index
>+ * @dpll: registered dpll pointer
>+ * @pin_idx: on success holds currently selected source pin index
>+ *
>+ * Dpll subsystem callback. Provides index of a source dpll is trying to lock
>+ * with.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_idx_get(const struct dpll_device *dpll, u32 *pin_idx)

Should return struct dpll_pin *. That should be the consitent driver api
handle for pin.


>+{
>+	struct ice_pf *pf = dpll_priv(dpll);
>+	struct ice_dpll *d;
>+	int ret = 0;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	*pin_idx = (u32)d->source_idx;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p d:%p, idx:%u\n",
>+		__func__, dpll, pf, d, *pin_idx);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_mode_get - get dpll's working mode
>+ * @dpll: registered dpll pointer
>+ * @mode: on success holds current working mode of dpll
>+ *
>+ * Dpll subsystem callback. Provides working mode of dpll.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_mode_get(const struct dpll_device *dpll,
>+			     enum dpll_mode *mode)
>+{
>+	struct ice_pf *pf = dpll_priv(dpll);
>+	struct ice_dpll *d;
>+	int ret = 0;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		ret = -EFAULT;
>+	else
>+		*mode = DPLL_MODE_AUTOMATIC;
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_mode_get - check if dpll's working mode is supported
>+ * @dpll: registered dpll pointer
>+ * @mode: mode to be checked for support
>+ *
>+ * Dpll subsystem callback. Provides information if working mode is supported
>+ * by dpll.
>+ *
>+ * Return:
>+ * * true - mode is supported
>+ * * false - mode is not supported
>+ */
>+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
>+				    const enum dpll_mode mode)
>+{
>+	struct ice_pf *pf = dpll_priv(dpll);
>+	struct ice_dpll *d;
>+	bool ret = true;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		ret = false;
>+	else
>+		if (mode != DPLL_MODE_AUTOMATIC)

"else if" on a single line.


>+			ret = false;
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_signal_type_supported - if pin signal type is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type being checked for support
>+ * @pin_type: type of a pin being checked
>+ *
>+ * Check is signal type is supported on given pin/dpll pair.
>+ *
>+ * Return:
>+ * * true - supported
>+ * * false - not supported
>+ */
>+static bool
>+ice_dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>+				   const struct dpll_pin *pin,
>+				   const enum dpll_pin_signal_type sig_type,
>+				   const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	bool supported = false;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (p) {
>+		if (test_bit(sig_type, &p->signal_type_mask))

Loose the nested if.


>+			supported = true;
>+	}
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return supported;
>+}
>+
>+/**
>+ * ice_dpll_rclk_signal_type_supported - if rclk source signal type is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type being checked for support
>+ *
>+ * Dpll subsystem callback. Check is signal type is supported on given pin/dpll
>+ * pair.
>+ *
>+ * Return:
>+ * * true - supported
>+ * * false - not supported
>+ */
>+static bool
>+ice_dpll_rclk_signal_type_supported(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin,
>+				    const enum dpll_pin_signal_type sig_type)
>+{
>+	const enum ice_dpll_pin_type t = ICE_DPLL_PIN_TYPE_RCLK_SOURCE;
>+
>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type, t);
>+}
>+
>+/**
>+ * ice_dpll_source_signal_type_supported - if source signal type is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type being checked for support
>+ *
>+ * Dpll subsystem callback. Check is signal type is supported on given pin/dpll
>+ * pair.
>+ *
>+ * Return:
>+ * * true - supported
>+ * * false - not supported
>+ */
>+static bool
>+ice_dpll_source_signal_type_supported(const struct dpll_device *dpll,
>+				      const struct dpll_pin *pin,
>+				      const enum dpll_pin_signal_type sig_type)
>+{
>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
>+						  ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_output_signal_type_supported - if output pin signal type is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type being checked
>+ *
>+ * Dpll subsystem callback. Check if signal type is supported on given pin/dpll
>+ * pair.
>+ *
>+ * Return:
>+ * * true - supported
>+ * * false - not supported
>+ */
>+static bool
>+ice_dpll_output_signal_type_supported(const struct dpll_device *dpll,
>+				      const struct dpll_pin *pin,
>+				      const enum dpll_pin_signal_type sig_type)
>+{
>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
>+						  ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_signal_type_get - get dpll's pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: on success - current signal type used on the pin
>+ * @pin_type: type of a pin being checked
>+ *
>+ * Find a pin and assign sig_type with its current signal type value.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_pin_signal_type_get(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					enum dpll_pin_signal_type *sig_type,
>+					const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	int ret = -ENODEV;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (p) {
>+		*sig_type = p->signal_type;
>+		ret = 0;
>+	}
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p\n",
>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_signal_type_get - get dpll's output pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: on success - current signal type value used on the pin
>+ *
>+ * Dpll subsystem callback. Find current signal type of given pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_output_signal_type_get(const struct dpll_device *dpll,
>+					   const struct dpll_pin *pin,
>+					   enum dpll_pin_signal_type *sig_type)
>+{
>+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_signal_type_set - set dpll pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type to be set
>+ * @pin_type: type of a pin being configured
>+ *
>+ * Handler for signal type modification on pins. Set signal type value for
>+ * a given pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_pin_signal_type_set(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					const enum dpll_pin_signal_type sig_type,
>+					const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	int ret = -EFAULT;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p)
>+		goto unlock;
>+	if (test_bit(sig_type, &p->signal_type_mask))
>+		ret = __ice_dpll_pin_signal_type_set(pf, p, pin_type, sig_type);
>+	else
>+		ret = -EINVAL;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL,  ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_signal_type_set - set dpll output pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type to be set
>+ *
>+ * Dpll subsystem callback. Wraps signal type modification handler.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_output_signal_type_set(const struct dpll_device *dpll,
>+				const struct dpll_pin *pin,
>+				const enum dpll_pin_signal_type sig_type)
>+{
>+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_mode_enable - enables a pin mode
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be set
>+ * @pin_type: type of pin being modified
>+ *
>+ * Handler for enabling the pin mode.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_pin_mode_enable(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin,
>+				    const enum dpll_pin_mode mode,
>+				    const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	int ret = -EFAULT;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (p)
>+		ret = ice_dpll_pin_mode_set(pf, p, pin_type, mode);
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL, ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_mode_enable - enable rclk-source pin mode
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be set
>+ *
>+ * Dpll subsystem callback. Enables given mode on recovered clock source type
>+ * pin.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_rclk_mode_enable(const struct dpll_device *dpll,
>+				     const struct dpll_pin *pin,
>+				     const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_output_mode_enable - enable output pin mode
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be set
>+ *
>+ * Dpll subsystem callback. Enables given mode on output type pin.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_output_mode_enable(const struct dpll_device *dpll,
>+				       const struct dpll_pin *pin,
>+				       const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>+					 ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_mode_enable - enable source pin mode
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be set
>+ *
>+ * Dpll subsystem callback. Enables given mode on source type pin.
>+ *
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int ice_dpll_source_mode_enable(const struct dpll_device *dpll,
>+				       const struct dpll_pin *pin,
>+				       const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>+					 ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_source_signal_type_get - get source pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @type: on success - source pin signal type
>+ *
>+ * Dpll subsystem callback. Get source pin signal type value.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_signal_type_get(const struct dpll_device *dpll,
>+					   const struct dpll_pin *pin,
>+					   enum dpll_pin_signal_type *sig_type)
>+{
>+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_source_signal_type_set - set dpll output pin signal type
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: signal type to be set
>+ *
>+ * dpll subsystem callback. Set source pin signal type value.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_source_signal_type_set(const struct dpll_device *dpll,
>+				const struct dpll_pin *pin,
>+				const enum dpll_pin_signal_type sig_type)
>+{
>+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_source_prio_get - get dpll's source prio
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @prio: on success - returns source priority on dpll
>+ *
>+ * Dpll subsystem callback. Handler for getting priority of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_prio_get(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin, u32 *prio)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll *d = NULL;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (*prio > ICE_DPLL_PRIO_MAX)
>+		return ret;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (!p)
>+		goto unlock;
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		goto unlock;
>+	*prio = d->input_prio[p->idx];
>+	ret = 0;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>+		__func__, dpll, pin, pf, ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_source_prio_set - set dpll source prio
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @prio: source priority to be set on dpll
>+ *
>+ * Dpll subsystem callback. Handler for setting priority of a source pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_source_prio_set(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin, const u32 prio)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll *d = NULL;
>+	struct ice_dpll_pin *p;
>+	int ret = -EINVAL;
>+
>+	if (prio > ICE_DPLL_PRIO_MAX)
>+		return ret;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (!p)
>+		goto unlock;
>+	d = ice_find_dpll(pf, dpll);
>+	if (!d)
>+		goto unlock;
>+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>+		__func__, dpll, pin, pf, ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_mode_active -  check if given pin's mode is active
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ * @pin_type: type of a pin to be checked
>+ *
>+ * Handler for checking if given mode is active on a pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_pin_mode_active(const struct dpll_device *dpll,
>+				     const struct dpll_pin *pin,
>+				     const enum dpll_pin_mode mode,
>+				     const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	bool ret = false;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+	if (!p)
>+		goto unlock;
>+	if (test_bit(mode, &p->mode_mask))
>+		ret = true;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_mode_active - check if rclk source pin's mode is active
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
>+ * on a recovered clock pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_rclk_mode_active(const struct dpll_device *dpll,
>+				      const struct dpll_pin *pin,
>+				      const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_output_mode_active - check if output pin's mode is active
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
>+ * on an output pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_output_mode_active(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>+					ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_mode_active - check if source pin's mode is active
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is active
>+ * on a source pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_source_mode_active(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>+					ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_pin_mode_supported - check if pin's mode is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ * @pin_type: type of a pin being checked
>+ *
>+ * Handler for checking if given mode is supported on a pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_pin_mode_supported(const struct dpll_device *dpll,
>+					const struct dpll_pin *pin,
>+					const enum dpll_pin_mode mode,
>+					const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	bool ret = false;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, pin_type);
>+
>+	if (!p)
>+		goto unlock;
>+	if (test_bit(mode, &p->mode_supported_mask))
>+		ret = true;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_mode_supported - check if rclk pin's mode is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>+ * supported on a clock recovery pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_rclk_mode_supported(const struct dpll_device *dpll,
>+					 const struct dpll_pin *pin,
>+					 const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>+					    ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_output_mode_supported - check if output pin's mode is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>+ * supported on an output pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_output_mode_supported(const struct dpll_device *dpll,
>+					   const struct dpll_pin *pin,
>+					   const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_source_mode_supported - check if source pin's mode is supported
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @mode: mode to be checked
>+ *
>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>+ * supported on a source pin.
>+ *
>+ * Return:
>+ * * true - mode is active
>+ * * false - mode is not active
>+ */
>+static bool ice_dpll_source_mode_supported(const struct dpll_device *dpll,
>+					   const struct dpll_pin *pin,
>+					   const enum dpll_pin_mode mode)
>+{
>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>+}
>+
>+/**
>+ * ice_dpll_rclk_pin_sig_type_get - get signal type of rclk pin
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @sig_type: on success - holds a signal type of a pin
>+ *
>+ * DPLL subsystem callback, provides signal type of clock recovery pin.
>+ *
>+ * Return:
>+ * * 0 - success, sig_type value is valid
>+ * * negative - error
>+ */
>+static int ice_dpll_rclk_pin_sig_type_get(const struct dpll_device *dpll,
>+					  const struct dpll_pin *pin,
>+					  enum dpll_pin_signal_type *sig_type)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	int ret = 0;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (!p) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	*sig_type = p->signal_type;
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p sig_type:%d ret:%d\n",
>+		__func__, dpll, pin, pf, *sig_type, ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_pin_net_if_index_get - get OS interface index callback
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @net_if_idx: on success - holds OS interface index
>+ *
>+ * dpll subsystem callback, obtains OS interface index and pass to the caller.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_rclk_pin_net_if_index_get(const struct dpll_device *dpll,
>+					      const struct dpll_pin *pin,
>+					      int *net_if_idx)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+
>+	if (!pf->vsi[0] || pf->vsi[0]->netdev)
>+		return -EAGAIN;
>+	*net_if_idx = pf->vsi[0]->netdev->ifindex;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_pin_select - select a recovered clock pin as a valid source
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ *
>+ * dpll subsystem callback, selects a pin for clock recovery,
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_rclk_pin_select(const struct dpll_device *dpll,
>+				    const struct dpll_pin *pin)
>+{
>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>+	struct ice_dpll_pin *p;
>+	u32 freq;
>+	int ret;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+	if (!p) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	if (!(p->flags & ICE_DPLL_RCLK_SOURCE_FLAG_EN)) {
>+		ret = -EPERM;
>+		goto unlock;
>+	}
>+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, p->idx, true, &freq);
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>+		__func__, dpll, pin, pf, ret);
>+
>+	return ret;
>+}
>+
>+static struct dpll_pin_ops ice_dpll_rclk_ops = {
>+	.mode_enable = ice_dpll_rclk_mode_enable,
>+	.mode_active = ice_dpll_rclk_mode_active,
>+	.mode_supported = ice_dpll_rclk_mode_supported,
>+	.signal_type_get = ice_dpll_rclk_pin_sig_type_get,
>+	.signal_type_supported = ice_dpll_rclk_signal_type_supported,
>+	.net_if_idx_get = ice_dpll_rclk_pin_net_if_index_get,
>+	.select = ice_dpll_rclk_pin_select,
>+};
>+
>+static struct dpll_pin_ops ice_dpll_source_ops = {
>+	.signal_type_get = ice_dpll_source_signal_type_get,
>+	.signal_type_set = ice_dpll_source_signal_type_set,
>+	.signal_type_supported = ice_dpll_source_signal_type_supported,
>+	.mode_active = ice_dpll_source_mode_active,
>+	.mode_enable = ice_dpll_source_mode_enable,
>+	.mode_supported = ice_dpll_source_mode_supported,
>+	.prio_get = ice_dpll_source_prio_get,
>+	.prio_set = ice_dpll_source_prio_set,
>+};
>+
>+static struct dpll_pin_ops ice_dpll_output_ops = {
>+	.signal_type_get = ice_dpll_output_signal_type_get,
>+	.signal_type_set = ice_dpll_output_signal_type_set,
>+	.signal_type_supported = ice_dpll_output_signal_type_supported,
>+	.mode_active = ice_dpll_output_mode_active,
>+	.mode_enable = ice_dpll_output_mode_enable,
>+	.mode_supported = ice_dpll_output_mode_supported,
>+};
>+
>+static struct dpll_device_ops ice_dpll_ops = {
>+	.lock_status_get = ice_dpll_lock_status_get,
>+	.source_pin_idx_get = ice_dpll_source_idx_get,
>+	.mode_get = ice_dpll_mode_get,
>+	.mode_supported = ice_dpll_mode_supported,
>+};
>+
>+/**
>+ * ice_dpll_release_info - release memory allocated for pins
>+ * @pf: board private structure
>+ *
>+ * Release memory allocated for pins by ice_dpll_init_info function.
>+ */
>+static void ice_dpll_release_info(struct ice_pf *pf)
>+{
>+	kfree(pf->dplls.inputs);
>+	pf->dplls.inputs = NULL;
>+	kfree(pf->dplls.outputs);
>+	pf->dplls.outputs = NULL;
>+	kfree(pf->dplls.eec.input_prio);
>+	pf->dplls.eec.input_prio = NULL;
>+	kfree(pf->dplls.pps.input_prio);
>+	pf->dplls.pps.input_prio = NULL;
>+}
>+
>+/**
>+ * ice_dpll_init_pins - initializes source or output pins information
>+ * @pf: Board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Init information about input or output pins, cache them in pins struct.
>+ */
>+static int ice_dpll_init_pins(struct ice_pf *pf,
>+			      const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>+	int ret = -EINVAL, num_pins, i;
>+	struct ice_hw *hw = &pf->hw;
>+	struct ice_dpll_pin *pins;
>+	bool input;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		input = true;
>+		pins = pf->dplls.inputs;
>+		num_pins = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		input = false;
>+		pins = pf->dplls.outputs;
>+		num_pins = pf->dplls.num_outputs;
>+	} else {
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < num_pins; i++) {
>+		pins[i].idx = i;
>+		pins[i].name = ice_cgu_get_pin_name(hw, i, input);
>+		pins[i].type = ice_cgu_get_pin_type(hw, i, input);
>+		set_bit(DPLL_PIN_MODE_CONNECTED,
>+			&pins[i].mode_supported_mask);
>+		set_bit(DPLL_PIN_MODE_DISCONNECTED,
>+			&pins[i].mode_supported_mask);
>+		if (input) {
>+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>+						      &de->input_prio[i]);
>+			if (ret)
>+				return ret;
>+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>+						      &dp->input_prio[i]);
>+			if (ret)
>+				return ret;
>+			set_bit(DPLL_PIN_MODE_SOURCE,
>+				&pins[i].mode_supported_mask);
>+		} else {
>+			set_bit(DPLL_PIN_MODE_OUTPUT,
>+				&pins[i].mode_supported_mask);
>+		}
>+		pins[i].signal_type_mask =
>+				ice_cgu_get_pin_sig_type_mask(hw, i, input);
>+		ret = ice_dpll_pin_update(hw, &pins[i], pin_type);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_release_pins - release pin's from dplls registered in subsystem
>+ * @dpll_eec: dpll_eec dpll pointer
>+ * @dpll_pps: dpll_pps dpll pointer
>+ * @pins: pointer to pins array
>+ * @count: number of pins
>+ *
>+ * Deregister and free pins of a given array of pins from dpll devices registered
>+ * in dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * positive - number of errors encounterd on pin's deregistration.
>+ */
>+static int
>+ice_dpll_release_pins(struct dpll_device *dpll_eec,
>+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
>+		      int count)
>+{
>+	int i, ret, err;
>+
>+	for (i = 0; i < count; i++) {
>+		struct ice_dpll_pin *p = &pins[i];
>+
>+		if (p && p->pin) {
>+			if (dpll_eec) {
>+				ret = dpll_pin_deregister(dpll_eec, p->pin);
>+				if (ret)
>+					err++;
>+			}
>+			if (dpll_pps) {
>+				ret = dpll_pin_deregister(dpll_pps, p->pin);
>+				if (ret)
>+					err++;
>+			}
>+			dpll_pin_free(p->pin);
>+			p->pin = NULL;
>+		}
>+	}
>+
>+	return err;
>+}
>+
>+/**
>+ * ice_dpll_register_pins - register pins with a dpll
>+ * @pf: board private structure
>+ * @dpll: registered dpll pointer
>+ * @pin_type: type of pins being registered
>+ *
>+ * Register source or output pins within given DPLL in a Linux dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_register_pins(struct ice_pf *pf, struct dpll_device *dpll,
>+		       const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *pins;
>+	struct dpll_pin_ops *ops;
>+	int ret, i, count;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		ops = &ice_dpll_source_ops;
>+		pins = pf->dplls.inputs;
>+		count = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		ops = &ice_dpll_output_ops;
>+		pins = pf->dplls.outputs;
>+		count = pf->dplls.num_outputs;
>+	} else {
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < count; i++) {
>+		pins[i].pin = dpll_pin_alloc(pins[i].name, pins[i].type);
>+		if (IS_ERR_OR_NULL(pins[i].pin))
>+			return -ENOMEM;
>+
>+		ret = dpll_pin_register(dpll, pins[i].pin, ops, pf);
>+		if (ret)
>+			return -ENOSPC;
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_register_shared_pins - register shared pins in DPLL subsystem
>+ * @pf: board private structure
>+ * @dpll_o: registered dpll pointer (owner)
>+ * @dpll: registered dpll pointer
>+ * @type: type of pins being registered
>+ *
>+ * Register pins from given owner dpll within given dpll in Linux dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_register_shared_pins(struct ice_pf *pf, struct dpll_device *dpll_o,
>+			      struct dpll_device *dpll,
>+			      const enum ice_dpll_pin_type pin_type)
>+{
>+	struct ice_dpll_pin *pins;
>+	struct dpll_pin_ops *ops;
>+	int ret, i, count;
>+
>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>+		ops = &ice_dpll_source_ops;
>+		pins = pf->dplls.inputs;
>+		count = pf->dplls.num_inputs;
>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>+		ops = &ice_dpll_output_ops;
>+		pins = pf->dplls.outputs;
>+		count = pf->dplls.num_outputs;
>+	} else {
>+		return -EINVAL;
>+	}
>+
>+	for (i = 0; i < count; i++) {
>+		ret = dpll_shared_pin_register(dpll_o, dpll, pins[i].name,
>+					       ops, pf);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_init_info - prepare pf's dpll information structure
>+ * @pf: board private structure
>+ *
>+ * Acquire (from HW) and set basic dpll information (on pf->dplls struct).
>+ *
>+ * Return:
>+ *  0 - success
>+ *  negative - error
>+ */
>+static int ice_dpll_init_info(struct ice_pf *pf)
>+{
>+	struct ice_aqc_get_cgu_abilities abilities;
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_hw *hw = &pf->hw;
>+	int ret, alloc_size;
>+
>+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>+	if (ret) {
>+		dev_err(ice_pf_to_dev(pf),
>+			"err:%d %s failed to read cgu abilities\n",
>+			ret, ice_aq_str(hw->adminq.sq_last_status));
>+		return ret;
>+	}
>+
>+	de->dpll_idx = abilities.eec_dpll_idx;
>+	dp->dpll_idx = abilities.pps_dpll_idx;
>+	d->num_inputs = abilities.num_inputs;
>+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!d->inputs)
>+		return -ENOMEM;
>+
>+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!de->input_prio)
>+		return -ENOMEM;
>+
>+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!dp->input_prio)
>+		return -ENOMEM;
>+
>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (ret)
>+		goto release_info;
>+
>+	d->num_outputs = abilities.num_outputs;
>+	alloc_size = sizeof(*d->outputs) * d->num_outputs;
>+	d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>+	if (!d->outputs)
>+		goto release_info;
>+
>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>+	if (ret)
>+		goto release_info;
>+
>+	dev_dbg(ice_pf_to_dev(pf), "%s - success, inputs:%u, outputs:%u\n", __func__,
>+		abilities.num_inputs, abilities.num_outputs);
>+
>+	return 0;
>+
>+release_info:
>+	dev_err(ice_pf_to_dev(pf),
>+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
>+		__func__, d->inputs, de->input_prio,
>+		dp->input_prio, d->outputs);
>+	ice_dpll_release_info(pf);
>+	return ret;
>+}
>+
>+/**
>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>+ * @pf: board private structure
>+ * @clock_id: holds generated clock_id
>+ *
>+ * Generates unique (per board) clock_id for allocation and search of dpll
>+ * devices in Linux dpll subsystem.
>+ */
>+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
>+{
>+	*clock_id = pci_get_dsn(pf->pdev);
>+}
>+
>+/**
>+ * ice_dpll_init_dpll
>+ * @pf: board private structure
>+ *
>+ * Allocate and register dpll in dpll subsystem.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - allocation fails
>+ */
>+static int ice_dpll_init_dpll(struct ice_pf *pf)
>+{
>+	struct device *dev = ice_pf_to_dev(pf);
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	u64 clock_id = 0;
>+	int ret = 0;
>+
>+	ice_generate_clock_id(pf, &clock_id);
>+
>+	de->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_EEC,
>+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
>+	if (!de->dpll) {
>+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (eec)\n");
>+		return -ENOMEM;
>+	}
>+
>+	dp->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_PPS,
>+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
>+	if (!dp->dpll) {
>+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (pps)\n");
>+		return -ENOMEM;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_update_state
>+ * @hw: board private structure
>+ * @d: pointer to queried dpll device
>+ *
>+ * Poll current state of dpll from hw and update ice_dpll struct.
>+ * Return:
>+ * * 0 - success
>+ * * negative - AQ failure
>+ */
>+static int ice_dpll_update_state(struct ice_hw *hw, struct ice_dpll *d)
>+{
>+	int ret;
>+
>+	ret = ice_get_cgu_state(hw, d->dpll_idx, d->prev_dpll_state,
>+				&d->source_idx, &d->ref_state, &d->eec_mode,
>+				&d->phase_offset, &d->dpll_state);
>+
>+	dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+		"update dpll=%d, src_idx:%u, state:%d, prev:%d\n",
>+		d->dpll_idx, d->source_idx,
>+		d->dpll_state, d->prev_dpll_state);
>+
>+	if (ret)
>+		dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+			"update dpll=%d state failed, ret=%d %s\n",
>+			d->dpll_idx, ret,
>+			ice_aq_str(hw->adminq.sq_last_status));
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_notify_changes - notify dpll subsystem about changes
>+ * @d: pointer do dpll
>+ *
>+ * Once change detected appropriate event is submitted to the dpll subsystem.
>+ */
>+static void ice_dpll_notify_changes(struct ice_dpll *d)
>+{
>+	if (d->prev_dpll_state != d->dpll_state) {
>+		d->prev_dpll_state = d->dpll_state;
>+		dpll_device_notify(d->dpll, DPLL_CHANGE_LOCK_STATUS);
>+	}
>+	if (d->prev_source_idx != d->source_idx) {
>+		d->prev_source_idx = d->source_idx;
>+		dpll_device_notify(d->dpll, DPLL_CHANGE_SOURCE_PIN);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_periodic_work - DPLLs periodic worker
>+ * @work: pointer to kthread_work structure
>+ *
>+ * DPLLs periodic worker is responsible for polling state of dpll.
>+ */
>+static void ice_dpll_periodic_work(struct kthread_work *work)
>+{
>+	struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>+	struct ice_dpll *de = &pf->dplls.eec;
>+	struct ice_dpll *dp = &pf->dplls.pps;
>+	int ret = 0;
>+
>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))

Why do you need to check the flag there, this would should not be
ever scheduled in case the flag was not set.


>+		return;
>+	mutex_lock(&d->lock);
>+	ret = ice_dpll_update_state(&pf->hw, de);
>+	if (!ret)
>+		ret = ice_dpll_update_state(&pf->hw, dp);
>+	if (ret) {
>+		d->cgu_state_acq_err_num++;
>+		/* stop rescheduling this worker */
>+		if (d->cgu_state_acq_err_num >
>+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
>+			dev_err(ice_pf_to_dev(pf),
>+				"EEC/PPS DPLLs periodic work disabled\n");
>+			return;
>+		}
>+	}
>+	mutex_unlock(&d->lock);
>+	ice_dpll_notify_changes(de);
>+	ice_dpll_notify_changes(dp);
>+
>+	/* Run twice a second or reschedule if update failed */
>+	kthread_queue_delayed_work(d->kworker, &d->work,
>+				   ret ? msecs_to_jiffies(10) :
>+				   msecs_to_jiffies(500));
>+}
>+
>+/**
>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>+ * @pf: board private structure
>+ *
>+ * Create and start DPLLs periodic worker.
>+ * Return:
>+ * * 0 - success
>+ * * negative - create worker failure
>+ */
>+static int ice_dpll_init_worker(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	struct kthread_worker *kworker;
>+
>+	ice_dpll_update_state(&pf->hw, &d->eec);
>+	ice_dpll_update_state(&pf->hw, &d->pps);
>+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>+	kworker = kthread_create_worker(0, "ice-dplls-%s",
>+					dev_name(ice_pf_to_dev(pf)));
>+	if (IS_ERR(kworker))
>+		return PTR_ERR(kworker);
>+	d->kworker = kworker;
>+	d->cgu_state_acq_err_num = 0;
>+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
>+
>+	return 0;
>+}
>+
>+/**
>+ * __ice_dpll_release - Disable the driver/HW support for DPLL and unregister
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * This function handles the cleanup work required from the initialization by
>+ * freeing resources and unregistering the dpll.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void __ice_dpll_release(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	struct ice_dpll *de = &d->eec;
>+	struct ice_dpll *dp = &d->pps;
>+	int ret;
>+
>+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
>+				    d->num_inputs);
>+	if (ret)
>+		dev_warn(ice_pf_to_dev(pf),
>+			 "pin deregister on PPS dpll err=%d\n", ret);
>+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
>+				    d->num_outputs);
>+	if (ret)
>+		dev_warn(ice_pf_to_dev(pf),
>+			 "pin deregister on PPS dpll err=%d\n", ret);
>+	ice_dpll_release_info(pf);
>+	if (dp->dpll) {
>+		dpll_device_unregister(dp->dpll);
>+		dpll_device_free(dp->dpll);
>+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
>+	}
>+
>+	if (de->dpll) {
>+		dpll_device_unregister(de->dpll);
>+		dpll_device_free(de->dpll);
>+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
>+	}
>+
>+	kthread_cancel_delayed_work_sync(&d->work);
>+	if (d->kworker) {
>+		kthread_destroy_worker(d->kworker);
>+		d->kworker = NULL;
>+		dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>+	}
>+}
>+
>+/**
>+ * ice_dpll_init - Initialize DPLLs support
>+ * @pf: board private structure
>+ *
>+ * Set up the device as owner of DPLLs registering them and pins connected
>+ * within Linux dpll subsystem. Allow userpsace to obtain state of DPLL
>+ * and handling of DPLL configuration requests.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+int ice_dpll_init(struct ice_pf *pf)
>+{
>+	struct ice_dplls *d = &pf->dplls;
>+	int err;
>+
>+	mutex_init(&d->lock);
>+	mutex_lock(&d->lock);

It is always odd to see the lock being created and locked right away.
Why do you need to lock it here?


>+	err = ice_dpll_init_info(pf);
>+	if (err)
>+		goto unlock;
>+	err = ice_dpll_init_dpll(pf);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_pins(pf, d->eec.dpll, ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_pins(pf, d->eec.dpll, ICE_DPLL_PIN_TYPE_OUTPUT);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>+	if (err)
>+		goto release;
>+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>+	if (err)
>+		goto release;
>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>+	err = ice_dpll_init_worker(pf);
>+	if (err)
>+		goto release;
>+	mutex_unlock(&d->lock);
>+	dev_dbg(ice_pf_to_dev(pf), "DPLLs init successful\n");
>+
>+	return err;
>+release:
>+	__ice_dpll_release(pf);
>+unlock:
>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>+	mutex_unlock(&d->lock);
>+	mutex_destroy(&d->lock);
>+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure\n");
>+
>+	return err;
>+}
>+
>+/**
>+ * ice_dpll_release - Disable the driver/HW support for DPLLs and unregister
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * This function handles the cleanup work required from the initialization by
>+ * freeing resources and unregistering the dpll.
>+ */
>+void ice_dpll_release(struct ice_pf *pf)
>+{
>+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>+		mutex_lock(&pf->dplls.lock);
>+		clear_bit(ICE_FLAG_DPLL, pf->flags);
>+		__ice_dpll_release(pf);
>+		mutex_unlock(&pf->dplls.lock);
>+		mutex_destroy(&pf->dplls.lock);
>+	}
>+}
>+
>+/**
>+ * ice_dpll_rclk_pin_init - init the pin info for recovered clock
>+ * @attr: structure with pin attributes
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+void ice_dpll_rclk_pin_init(struct ice_dpll_pin *p)
>+{
>+	p->flags = ICE_DPLL_RCLK_SOURCE_FLAG_EN;
>+	p->type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>+	set_bit(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ, &p->signal_type_mask);
>+	set_bit(DPLL_PIN_MODE_CONNECTED,	  &p->mode_supported_mask);
>+	set_bit(DPLL_PIN_MODE_DISCONNECTED,	  &p->mode_supported_mask);
>+	set_bit(DPLL_PIN_MODE_SOURCE,		  &p->mode_supported_mask);
>+	ice_dpll_pin_update(0, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>+}
>+
>+/**
>+ * __ice_dpll_rclk_release - unregister the recovered pin for dpll device
>+ * @pf: board private structure
>+ *
>+ * This function handles the cleanup work required from the initialization by
>+ * freeing resources and unregistering the recovered pin.
>+ */
>+void __ice_dpll_rclk_release(struct ice_pf *pf)
>+{
>+	int ret = 0;
>+
>+	if (pf->dplls.eec.dpll) {
>+		if (pf->dplls.rclk[0].pin)
>+			ret = dpll_pin_deregister(pf->dplls.eec.dpll,
>+						  pf->dplls.rclk[0].pin);
>+		dpll_pin_free(pf->dplls.rclk->pin);
>+		kfree(pf->dplls.rclk);
>+	}
>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
>+}
>+
>+/**
>+ * ice_dpll_rclk_pins_init - init the pin for recovered clock
>+ * @pf: board private structure
>+ * @first_parent: pointer to a first parent pin
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+int ice_dpll_rclk_pins_init(struct ice_pf *pf, struct ice_dpll_pin *first_parent)
>+{
>+	struct ice_dpll_pin *parent, *p;
>+	char *name;
>+	int i, ret;
>+
>+	if (pf->dplls.rclk)
>+		return -EEXIST;
>+	pf->dplls.rclk = kcalloc(pf->dplls.num_rclk, sizeof(*pf->dplls.rclk),
>+				 GFP_KERNEL);
>+	if (!pf->dplls.rclk)
>+		goto release;
>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>+		p = &pf->dplls.rclk[i];
>+		if (!p)
>+			goto release;
>+		ice_dpll_rclk_pin_init(p);
>+		parent = first_parent + i;
>+		if (!parent)
>+			goto release;
>+		p->idx = i;
>+		name = kcalloc(DPLL_PIN_DESC_LEN, sizeof(*p->name), GFP_KERNEL);
>+		if (!name)
>+			goto release;
>+		snprintf(name, DPLL_PIN_DESC_LEN - 1, "%s-%u",
>+			 parent->name, pf->hw.pf_id);
>+		p->name = name;
>+		p->pin = dpll_pin_alloc(p->name, p->type);
>+		if (IS_ERR_OR_NULL(p->pin))
>+			goto release;
>+		ret = dpll_muxed_pin_register(pf->dplls.eec.dpll, parent->name,
>+					      p->pin, &ice_dpll_rclk_ops, pf);
>+		if (ret)
>+			goto release;
>+		ret = dpll_shared_pin_register(pf->dplls.eec.dpll,
>+					       pf->dplls.pps.dpll,
>+					       p->name,
>+					       &ice_dpll_rclk_ops, pf);
>+		if (ret)
>+			goto release;
>+	}
>+
>+	return ret;
>+release:
>+	dev_dbg(ice_pf_to_dev(pf),
>+		"%s releasing - p: %p, parent:%p, p->pin:%p name:%s, ret:%d\n",
>+		__func__, p, parent, p->pin, name, ret);
>+	__ice_dpll_rclk_release(pf);
>+	return -ENOMEM;
>+}
>+
>+/**
>+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
>+ * @pf: board private structure
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf)
>+{
>+	u64 clock_id = 0;
>+
>+	ice_generate_clock_id(pf, &clock_id);
>+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,

I have to say I'm a bit lost in this code. Why exactly do you need this
here? Looks like the pointer was set in ice_dpll_init_dpll().

Or, is that in case of a different PF instantiating the DPLL instances?
If yes, I'm pretty sure what it is wrong. What is the PF which did
instanticate those unbinds? You have to share the dpll instance,
refcount it.

Btw, you have a problem during init as well, as the order matters. What
if the other function probes only after executing this? You got -EFAULT
here and bail out.

In mlx5, I also share one dpll instance between 2 PFs. What I do is I
create mlx5-dpll instance which is refcounted, created by first probed
PF and removed by the last one. In mlx5 case, the PFs are equal, nobody
is an owner of the dpll. In your case, I think it is different. So
probably better to implement the logic in driver then in the dpll core.

Then you don't need dpll_device_get_by_clock_id at all. If you decide to
implement that in dpll core, I believe that there should be some
functions like:
dpll = dpll_device_get(ops, clock_id, ...)  - to create/get reference
dpll_device_put(dpll)                       - to put reference/destroy

First caller of dpll_device_get() actually makes dpll to instantiate the
device.



>+							 DPLL_TYPE_EEC, 0);
>+	if (!pf->dplls.eec.dpll)
>+		return -EFAULT;
>+	pf->dplls.pps.dpll = dpll_device_get_by_clock_id(clock_id,
>+							 DPLL_TYPE_PPS, 0);
>+	if (!pf->dplls.pps.dpll)
>+		return -EFAULT;
>+
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_parent_pins_init - initialize the recovered clock parent pins
>+ * @pf: board private structure
>+ * @base_rclk_idx: number of first recovered clock pin in DPLL
>+ *
>+ * This function shall be executed only if ICE_FLAG_DPLL feature is not

Feature? It's a flag.


>+ * supported.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+static int ice_dpll_rclk_parent_pins_init(struct ice_pf *pf, u8 base_rclk_idx)
>+{
>+	int i;
>+
>+	if (pf->dplls.inputs)
>+		return -EINVAL;
>+	pf->dplls.inputs = kcalloc(pf->dplls.num_rclk,
>+				   sizeof(*pf->dplls.inputs), GFP_KERNEL);
>+
>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>+		const char *desc;
>+
>+		desc = ice_cgu_get_pin_name(&pf->hw, base_rclk_idx + i, true);
>+		if (!desc)
>+			return -EINVAL;
>+		pf->dplls.inputs[i].name = desc;
>+	}
>+	return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_init - Enable support for DPLL's PHY clock recovery
>+ * @pf: board private structure
>+ *
>+ * Context:
>+ * Acquires a pf->dplls.lock. If PF is not an owner of DPLL it shall find and
>+ * connect its pins with the device dpll.
>+ *
>+ * This function handles enablement of PHY clock recovery part for timesync
>+ * capabilities.
>+ * Prepares and initalizes resources required to register its PHY clock sources
>+ * within DPLL subsystem.
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure
>+ */
>+int ice_dpll_rclk_init(struct ice_pf *pf)
>+{
>+	struct ice_dpll_pin *first_parent = NULL;
>+	u8 base_rclk_idx;
>+	int ret;
>+
>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &base_rclk_idx,
>+					&pf->dplls.num_rclk);
>+	if (ret)
>+		return ret;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>+		ret = ice_dpll_rclk_find_dplls(pf);
>+		dev_dbg(ice_pf_to_dev(pf), "ecc:%p, pps:%p\n",
>+			pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>+		if (ret)
>+			goto unlock;
>+		ret = ice_dpll_rclk_parent_pins_init(pf, base_rclk_idx);
>+		if (ret)
>+			goto unlock;
>+		first_parent = &pf->dplls.inputs[0];
>+	} else {
>+		first_parent = &pf->dplls.inputs[base_rclk_idx];
>+	}
>+	if (!first_parent) {
>+		ret = -EFAULT;
>+		goto unlock;
>+	}
>+	ret = ice_dpll_rclk_pins_init(pf, first_parent);
>+unlock:
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK init ret=%d\n", ret);
>+
>+	return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_release - Disable the support for DPLL's PHY clock recovery
>+ * @pf: board private structure
>+ *
>+ * Context:
>+ * Acquires a pf->dplls.lock. Requires dplls to be present, must be called
>+ * before dplls are realesed.
>+ *
>+ * This function handles the cleanup work of resources allocated for enablement
>+ * of PHY recovery clock mechanics.
>+ * Unregisters RCLK pins and frees pin's memory allocated by ice_dpll_rclk_init.
>+ */
>+void ice_dpll_rclk_release(struct ice_pf *pf)
>+{
>+	int i, ret = 0;
>+
>+	if (!pf->dplls.rclk)
>+		return;
>+
>+	mutex_lock(&pf->dplls.lock);
>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>+		if (pf->dplls.rclk[i].pin) {
>+			dpll_pin_deregister(pf->dplls.eec.dpll,
>+					    pf->dplls.rclk[i].pin);
>+			dpll_pin_deregister(pf->dplls.pps.dpll,
>+					    pf->dplls.rclk[i].pin);
>+			dpll_pin_free(pf->dplls.rclk[i].pin);
>+			pf->dplls.rclk[i].pin = NULL;
>+		}
>+		kfree(pf->dplls.rclk[i].name);
>+		pf->dplls.rclk[i].name = NULL;
>+	}
>+	/* inputs were prepared only for RCLK, release them here */
>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>+		kfree(pf->dplls.inputs);
>+		pf->dplls.inputs = NULL;
>+	}
>+	kfree(pf->dplls.rclk);
>+	pf->dplls.rclk = NULL;
>+	mutex_unlock(&pf->dplls.lock);
>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
>+}
>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
>new file mode 100644
>index 000000000000..3390d60f2fab
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>@@ -0,0 +1,99 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#ifndef _ICE_DPLL_H_
>+#define _ICE_DPLL_H_
>+
>+#include "ice.h"
>+
>+#define ICE_DPLL_PRIO_MAX	0xF
>+
>+/** ice_dpll_pin - store info about pins
>+ * @pin: dpll pin structure
>+ * @flags: pin flags returned from HW
>+ * @idx: ice pin private idx
>+ * @type: type of a pin
>+ * @signal_type: current signal type
>+ * @signal_type_mask: signal types supported
>+ * @freq: current frequency of a pin
>+ * @mode_mask: current pin modes as bitmask
>+ * @mode_supported_mask: supported pin modes
>+ * @name: pin name
>+ */
>+struct ice_dpll_pin {
>+	struct dpll_pin *pin;
>+#define ICE_DPLL_RCLK_SOURCE_FLAG_EN	BIT(0)
>+	u8 flags;
>+	u8 idx;
>+	enum dpll_pin_type type;
>+	enum dpll_pin_signal_type signal_type;
>+	unsigned long signal_type_mask;
>+	u32 freq;
>+	unsigned long mode_mask;
>+	unsigned long mode_supported_mask;
>+	const char *name;
>+};
>+
>+/** ice_dpll - store info required for DPLL control
>+ * @dpll: pointer to dpll dev
>+ * @dpll_idx: index of dpll on the NIC
>+ * @source_idx: source currently selected
>+ * @prev_source_idx: source previously selected
>+ * @ref_state: state of dpll reference signals
>+ * @eec_mode: eec_mode dpll is configured for
>+ * @phase_offset: phase delay of a dpll
>+ * @input_prio: priorities of each input
>+ * @dpll_state: current dpll sync state
>+ * @prev_dpll_state: last dpll sync state
>+ */
>+struct ice_dpll {
>+	struct dpll_device *dpll;
>+	int dpll_idx;
>+	u8 source_idx;
>+	u8 prev_source_idx;
>+	u8 ref_state;
>+	u8 eec_mode;
>+	s64 phase_offset;
>+	u8 *input_prio;
>+	enum ice_cgu_state dpll_state;
>+	enum ice_cgu_state prev_dpll_state;
>+};
>+
>+/** ice_dplls - store info required for CCU (clock controlling unit)
>+ * @kworker: periodic worker
>+ * @work: periodic work
>+ * @lock: locks access to configuration of a dpll
>+ * @eec: pointer to EEC dpll dev
>+ * @pps: pointer to PPS dpll dev
>+ * @inputs: input pins pointer
>+ * @outputs: output pins pointer
>+ * @rclk: recovered pins pointer
>+ * @num_inputs: number of input pins available on dpll
>+ * @num_outputs: number of output pins available on dpll
>+ * @num_rclk: number of recovered clock pins available on dpll
>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>+ */
>+struct ice_dplls {
>+	struct kthread_worker *kworker;
>+	struct kthread_delayed_work work;
>+	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 *rclk;
>+	u32 num_inputs;
>+	u32 num_outputs;
>+	u8 num_rclk;
>+	int cgu_state_acq_err_num;
>+};
>+
>+int ice_dpll_init(struct ice_pf *pf);
>+
>+void ice_dpll_release(struct ice_pf *pf);
>+
>+int ice_dpll_rclk_init(struct ice_pf *pf);
>+
>+void ice_dpll_rclk_release(struct ice_pf *pf);
>+
>+#endif
>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
>index a9a7f8b52140..8b65f4ad245e 100644
>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>@@ -4896,6 +4896,12 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
> 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
> 		ice_ptp_init(pf);
> 
>+	if (ice_is_feature_supported(pf, ICE_F_CGU))
>+		ice_dpll_init(pf);
>+
>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>+		ice_dpll_rclk_init(pf);
>+
> 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
> 		ice_gnss_init(pf);
> 
>@@ -5078,6 +5084,10 @@ static void ice_remove(struct pci_dev *pdev)
> 		ice_ptp_release(pf);
> 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
> 		ice_gnss_exit(pf);
>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>+		ice_dpll_rclk_release(pf);
>+	if (ice_is_feature_supported(pf, ICE_F_CGU))
>+		ice_dpll_release(pf);
> 	if (!ice_is_safe_mode(pf))
> 		ice_remove_arfs(pf);
> 	ice_setup_mc_magic_wake(pf);
>-- 
>2.30.2
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-01-17 18:00   ` Vadim Fedorenko
@ 2023-01-19 17:16     ` Jiri Pirko
  -1 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-19 17:16 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk, Milena Olech,
	Michal Michalik

Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>DPLL framework is used to represent and configure DPLL devices
>in systems. Each device that has DPLL and can configure sources
>and outputs can use this framework. Netlink interface is used to
>provide configuration data and to receive notification messages
>about changes in the configuration or status of DPLL device.
>Inputs and outputs of the DPLL device are represented as special
>objects which could be dynamically added to and removed from DPLL
>device.
>
>Co-developed-by: Milena Olech <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
>---
> MAINTAINERS                 |    8 +
> drivers/Kconfig             |    2 +
> drivers/Makefile            |    1 +
> drivers/dpll/Kconfig        |    7 +
> drivers/dpll/Makefile       |    9 +
> drivers/dpll/dpll_core.c    | 1010 +++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_core.h    |  105 ++++
> drivers/dpll/dpll_netlink.c |  883 ++++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h |   24 +
> include/linux/dpll.h        |  282 ++++++++++
> include/uapi/linux/dpll.h   |  294 ++++++++++
> 11 files changed, 2625 insertions(+)
> create mode 100644 drivers/dpll/Kconfig
> create mode 100644 drivers/dpll/Makefile
> create mode 100644 drivers/dpll/dpll_core.c
> create mode 100644 drivers/dpll/dpll_core.h
> create mode 100644 drivers/dpll/dpll_netlink.c
> create mode 100644 drivers/dpll/dpll_netlink.h
> create mode 100644 include/linux/dpll.h
> create mode 100644 include/uapi/linux/dpll.h
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index f82dd8d43c2b..de8a10b21ce8 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6411,6 +6411,14 @@ F:	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
> F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
> F:	drivers/net/ethernet/freescale/dpaa2/dpsw*
> 
>+DPLL CLOCK SUBSYSTEM
>+M:	Vadim Fedorenko <vadfed@fb.com>
>+L:	netdev@vger.kernel.org
>+S:	Maintained
>+F:	drivers/dpll/*
>+F:	include/net/dpll.h
>+F:	include/uapi/linux/dpll.h
>+
> DRBD DRIVER
> M:	Philipp Reisner <philipp.reisner@linbit.com>
> M:	Lars Ellenberg <lars.ellenberg@linbit.com>
>diff --git a/drivers/Kconfig b/drivers/Kconfig
>index 968bd0a6fd78..453df9e1210d 100644
>--- a/drivers/Kconfig
>+++ b/drivers/Kconfig
>@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
> 
> source "drivers/hte/Kconfig"
> 
>+source "drivers/dpll/Kconfig"
>+
> endmenu
>diff --git a/drivers/Makefile b/drivers/Makefile
>index bdf1c66141c9..7cbee58bc692 100644
>--- a/drivers/Makefile
>+++ b/drivers/Makefile
>@@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER)		+= counter/
> obj-$(CONFIG_MOST)		+= most/
> obj-$(CONFIG_PECI)		+= peci/
> obj-$(CONFIG_HTE)		+= hte/
>+obj-$(CONFIG_DPLL)		+= dpll/
>diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
>new file mode 100644
>index 000000000000..a4cae73f20d3
>--- /dev/null
>+++ b/drivers/dpll/Kconfig
>@@ -0,0 +1,7 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+#
>+# Generic DPLL drivers configuration
>+#
>+
>+config DPLL
>+  bool
>diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>new file mode 100644
>index 000000000000..b18cf848a010
>--- /dev/null
>+++ b/drivers/dpll/Makefile
>@@ -0,0 +1,9 @@
>+# SPDX-License-Identifier: GPL-2.0
>+#
>+# Makefile for DPLL drivers.
>+#
>+
>+obj-$(CONFIG_DPLL)          += dpll_sys.o
>+dpll_sys-y                  += dpll_core.o
>+dpll_sys-y                  += dpll_netlink.o
>+
>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..fec534f17827
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,1010 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ *  dpll_core.c - Generic DPLL Management class support.
>+ *
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>+
>+#include <linux/device.h>
>+#include <linux/err.h>
>+#include <linux/slab.h>
>+#include <linux/string.h>
>+
>+#include "dpll_core.h"
>+
>+/**
>+ * struct dpll_pin - structure for a dpll pin
>+ * @idx:		unique id number for each pin
>+ * @parent_pin:		parent pin
>+ * @type:		type of the pin
>+ * @ops:		operations this &dpll_pin supports
>+ * @priv:		pointer to private information of owner
>+ * @ref_dplls:		array of registered dplls
>+ * @description:	name to distinguish the pin
>+ */
>+struct dpll_pin {
>+	u32 idx;
>+	struct dpll_pin *parent_pin;
>+	enum dpll_pin_type type;
>+	struct dpll_pin_ops *ops;
>+	void *priv;
>+	struct xarray ref_dplls;
>+	char description[DPLL_PIN_DESC_LEN];
>+};
>+static DEFINE_MUTEX(dpll_device_xa_lock);
>+
>+static DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
>+#define DPLL_REGISTERED		XA_MARK_1
>+#define PIN_REGISTERED		XA_MARK_1

DPLL_PIN_REGISTERED


>+
>+#define ASSERT_DPLL_REGISTERED(d)                                           \
>+	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>+#define ASSERT_DPLL_NOT_REGISTERED(d)                                      \
>+	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>+
>+struct dpll_pin_ref {
>+	struct dpll_device *dpll;
>+	struct dpll_pin_ops *ops;
>+	void *priv;
>+};
>+
>+/**
>+ * dpll_device_get_by_id - find dpll device by it's id
>+ * @id: id of searched dpll
>+ *
>+ * Return: dpll_device struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_device_get_by_id(int id)
>+{
>+	struct dpll_device *dpll = NULL;
>+
>+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>+		dpll = xa_load(&dpll_device_xa, id);
>+
>+	return dpll;
>+}
>+
>+/**
>+ * dpll_device_get_by_name - find dpll device by it's id
>+ * @name: name of searched dpll
>+ *
>+ * Return: dpll_device struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_device_get_by_name(const char *name)
>+{
>+	struct dpll_device *dpll, *ret = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>+		if (!strcmp(dev_name(&dpll->dev), name)) {
>+			ret = dpll;
>+			break;
>+		}
>+	}
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return ret;
>+}
>+
>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,

Hmm, don't you want to put an owner module as an arg here as well? I
don't see how could 2 modules sanely work with the same dpll instance.


>+						enum dpll_type type, u8 idx)
>+{
>+	struct dpll_device *dpll, *ret = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>+		if (dpll->clock_id == clock_id) {
>+			if (dpll->type == type) {
>+				if (dpll->dev_driver_idx == idx) {
>+					ret = dpll;
>+					break;
>+				}
>+			}
>+		}
>+	}
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
>+
>+static void dpll_device_release(struct device *dev)
>+{
>+	struct dpll_device *dpll;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	dpll = to_dpll_device(dev);
>+	dpll_device_unregister(dpll);
>+	mutex_unlock(&dpll_device_xa_lock);
>+	dpll_device_free(dpll);
>+}
>+
>+static struct class dpll_class = {
>+	.name = "dpll",
>+	.dev_release = dpll_device_release,

Why do you want to do this? Why the driver cannot do
dpll_device_unregister/free() manually. I think it makes things easier
to read then to rely on dev garbage collector.


>+};
>+
>+struct dpll_device
>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>+		   u8 dev_driver_idx, void *priv, struct device *parent)
>+{
>+	struct dpll_device *dpll;
>+	int ret;
>+
>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>+	if (!dpll)
>+		return ERR_PTR(-ENOMEM);
>+
>+	mutex_init(&dpll->lock);
>+	dpll->ops = ops;
>+	dpll->dev.class = &dpll_class;
>+	dpll->parent = parent;
>+	dpll->type = type;
>+	dpll->dev_driver_idx = dev_driver_idx;
>+	dpll->clock_id = clock_id;
>+	dpll->clock_class = clock_class;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>+		       xa_limit_16b, GFP_KERNEL);
>+	if (ret)
>+		goto error;
>+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
>+		     dev_driver_idx);
>+	dpll->priv = priv;
>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);

What is exactly the point of using this mark?


>+	mutex_unlock(&dpll_device_xa_lock);
>+	dpll_notify_device_create(dpll);
>+
>+	return dpll;
>+
>+error:
>+	mutex_unlock(&dpll_device_xa_lock);
>+	kfree(dpll);
>+	return ERR_PTR(ret);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_alloc);
>+
>+void dpll_device_free(struct dpll_device *dpll)
>+{
>+	WARN_ON_ONCE(!dpll);
>+	WARN_ON_ONCE(!xa_empty(&dpll->pins));
>+	xa_destroy(&dpll->pins);
>+	mutex_destroy(&dpll->lock);
>+	kfree(dpll);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_free);
>+
>+/**
>+ * dpll_device_unregister - unregister dpll device
>+ * @dpll: registered dpll pointer
>+ *
>+ * Note: It does not free the memory
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll)
>+{
>+	ASSERT_DPLL_REGISTERED(dpll);
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_erase(&dpll_device_xa, dpll->id);
>+	dpll_notify_device_delete(dpll);

Why do you need to hold the lock for notify?


>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_unregister);
>+
>+/**
>+ * dpll_id - return dpll id
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: dpll id.
>+ */
>+u32 dpll_id(struct dpll_device *dpll)
>+{
>+	return dpll->id;
>+}
>+
>+/**
>+ * dpll_pin_idx - return index of a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ *
>+ * Return: index of a pin or PIN_IDX_INVALID if not found.
>+ */
>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)
>+{
>+	struct dpll_pin *pos;
>+	unsigned long index;
>+
>+	xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED) {
>+		if (pos == pin)

What is the purpose of the lookup for the pin struct you pass as an arg?


>+			return pin->idx;
>+	}
>+
>+	return PIN_IDX_INVALID;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_idx);
>+
>+const char *dpll_dev_name(struct dpll_device *dpll)
>+{
>+	return dev_name(&dpll->dev);
>+}
>+
>+struct dpll_pin *dpll_pin_alloc(const char *description,
>+				const enum dpll_pin_type pin_type)

s/pin_type/type/


>+{
>+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
>+
>+	if (!pin)
>+		return ERR_PTR(-ENOMEM);
>+	if (pin_type <= DPLL_PIN_TYPE_UNSPEC ||
>+	    pin_type > DPLL_PIN_TYPE_MAX)
>+		return ERR_PTR(-EINVAL);

I think this check is not needed here. If driver is passing something
else, it is buggy. Idk. If you decide to leave this, put it in WARN_ON


>+
>+	strncpy(pin->description, description, DPLL_PIN_DESC_LEN);

kstrdup. Please treat the rest of the strings like that. No need to
limit the string names.


>+	pin->description[DPLL_PIN_DESC_LEN - 1] = '\0';
>+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
>+	pin->type = pin_type;
>+
>+	return pin;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_alloc);
>+
>+static int dpll_alloc_pin_on_xa(struct xarray *pins, struct dpll_pin *pin)
>+{
>+	struct dpll_pin *pos;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each(pins, index, pos) {
>+		if (pos == pin ||
>+		    !strncmp(pos->description, pin->description,
>+			     DPLL_PIN_DESC_LEN))

WARN_ON. The driver is buggy if it does something like this.


>+			return -EEXIST;
>+	}
>+
>+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
>+	if (!ret)
>+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);

What is exactly the point of having this mark?


>+
>+	return ret;
>+}
>+
>+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device *dpll,
>+			    struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin_ref *ref, *pos;
>+	unsigned long index;
>+	u32 idx;
>+
>+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
>+	if (!ref)
>+		return -ENOMEM;
>+	ref->dpll = dpll;
>+	ref->ops = ops;
>+	ref->priv = priv;
>+	if (!xa_empty(&pin->ref_dplls)) {

Pointless check. Just do iterate.


>+		xa_for_each(&pin->ref_dplls, index, pos) {
>+			if (pos->dpll == ref->dpll)
>+				return -EEXIST;
>+		}
>+	}
>+
>+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
>+}
>+
>+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device *dpll)
>+{
>+	struct dpll_pin_ref *pos;
>+	unsigned long index;
>+
>+	xa_for_each(&pin->ref_dplls, index, pos) {
>+		if (pos->dpll == dpll) {
>+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
>+			break;
>+		}
>+	}
>+}
>+
>+static int pin_deregister_from_xa(struct xarray *xa_pins, struct dpll_pin *pin)

1) dpll_ prefix
2) "deregister" is odd name
3) why don't you have it next to dpll_alloc_pin_on_xa() as it is a
   symmetric function?
4) Why exactly just xa_erase() would not do?

>+{
>+	struct dpll_pin *pos;
>+	unsigned long index;
>+
>+	xa_for_each(xa_pins, index, pos) {
>+		if (pos == pin) {
>+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));

You have an odd pattern of functions getting pin as an arg then
doing lookup for the same pin. I have to be missing to see some
black magic here :O


>+			return 0;
>+		}
>+	}
>+
>+	return -ENXIO;
>+}
>+
>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      struct dpll_pin_ops *ops, void *priv)
>+{
>+	int ret;
>+
>+	if (!pin || !ops)
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll->lock);
>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>+	if (!ret) {
>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>+		if (ret)
>+			pin_deregister_from_xa(&dpll->pins, pin);
>+	}
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>+
>+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins, int idx)
>+{
>+	struct dpll_pin *pos;
>+	unsigned long index;
>+
>+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
>+		if (pos->idx == idx)
>+			return pos;
>+	}
>+
>+	return NULL;
>+}
>+
>+/**
>+ * dpll_pin_get_by_idx - find a pin by its index
>+ * @dpll: dpll device pointer
>+ * @idx: index of pin
>+ *
>+ * Allows multiple driver instances using one physical DPLL to find
>+ * and share pin already registered with existing dpll device.
>+ *
>+ * Return: pointer if pin was found, NULL otherwise.
>+ */
>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
>+{
>+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
>+}
>+
>+	struct dpll_pin
>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char *description)
>+{
>+	struct dpll_pin *pos, *pin = NULL;
>+	unsigned long index;
>+
>+	xa_for_each(&dpll->pins, index, pos) {
>+		if (!strncmp(pos->description, description,
>+			     DPLL_PIN_DESC_LEN)) {
>+			pin = pos;
>+			break;
>+		}
>+	}
>+
>+	return pin;
>+}
>+
>+int
>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>+			 struct dpll_device *dpll,
>+			 const char *shared_pin_description,

I don't follow why you need to pass the string. You have struct dpll_pin
* in the driver. Pass that instead, avoid string to refer to kernel
object. But this is something I wrote multiple times.


>+			 struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	mutex_lock(&dpll_pin_owner->lock);
>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>+					  shared_pin_description);
>+	if (!pin) {
>+		ret = -EINVAL;
>+		goto unlock;
>+	}
>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>+unlock:
>+	mutex_unlock(&dpll_pin_owner->lock);
>+
>+	return ret;

I don't understand why there should be a separate function to register
the shared pin. As I see it, there is a pin object that could be
registered with 2 or more dpll devices. What about having:

pin = dpll_pin_alloc(desc, type, ops, priv)
dpll_pin_register(dpll_1, pin);
dpll_pin_register(dpll_2, pin);
dpll_pin_register(dpll_3, pin);

Then one pin will we in 3 xa_arrays for 3 dplls.


>+}
>+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);
>+
>+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin)

s/deregister/unregister. Be consistent in naming the functions.


>+{
>+	int ret = 0;
>+
>+	if (xa_empty(&dpll->pins))
>+		return -ENOENT;

Remove this check

>+
>+	mutex_lock(&dpll->lock);
>+	ret = pin_deregister_from_xa(&dpll->pins, pin);
>+	if (!ret)
>+		dpll_pin_ref_del(pin, dpll);
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_DEL);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_deregister);
>+
>+void dpll_pin_free(struct dpll_pin *pin)
>+{
>+	if (!xa_empty(&pin->ref_dplls))
>+		return;
>+
>+	xa_destroy(&pin->ref_dplls);
>+	kfree(pin);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_free);
>+
>+int dpll_muxed_pin_register(struct dpll_device *dpll,
>+			    const char *parent_pin_description,

Again, pass struct dpll_pin *parent, not a string.


>+			    struct dpll_pin *pin,
>+			    struct dpll_pin_ops *ops, void *priv)

Why this is a separate function? Why can't we have one function
say __dpll_pin_register()
which is called from
dpll_pin_register() - parent == null
or
dpll_muxed_pin_register() - parent == valid parent pointer
?



>+{
>+	struct dpll_pin *parent_pin;
>+	int ret;
>+
>+	if (!parent_pin_description || !pin)
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll->lock);
>+	parent_pin = dpll_pin_get_by_description(dpll, parent_pin_description);
>+	if (!parent_pin)
>+		return -EINVAL;
>+	if (parent_pin->type != DPLL_PIN_TYPE_MUX)
>+		return -EPERM;
>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>+	if (!ret)
>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>+	if (!ret)
>+		pin->parent_pin = parent_pin;
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
>+
>+/**
>+ * dpll_pin_first - get first registered pin
>+ * @dpll: registered dpll pointer
>+ * @index: found pin index (out)
>+ *
>+ * Return: dpll_pin struct if found, NULL otherwise.
>+ */
>+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long *index)
>+{
>+	*index = 0;
>+
>+	return xa_find(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>+}
>+
>+/**
>+ * dpll_pin_next - get next registered pin to the relative pin
>+ * @dpll: registered dpll pointer
>+ * @index: relative pin index (in and out)
>+ *
>+ * Return: dpll_pin struct if found, NULL otherwise.
>+ */
>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index)
>+{
>+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>+}
>+
>+/**
>+ * dpll_first - get first registered dpll device
>+ * @index: found dpll index (out)
>+ *
>+ * Return: dpll_device struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_first(unsigned long *index)
>+{
>+	*index = 0;
>+
>+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>+}
>+
>+/**
>+ * dpll_pin_next - get next registered dpll device to the relative pin
>+ * @index: relative dpll index (in and out)
>+ *
>+ * Return: dpll_pin struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_next(unsigned long *index)
>+{
>+	return xa_find_after(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>+}
>+
>+static struct dpll_pin_ref
>+*dpll_pin_find_ref(const struct dpll_device *dpll, const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long index;
>+
>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>+		if (ref->dpll != dpll)
>+			continue;
>+		else
>+			return ref;
>+	}
>+
>+	return NULL;
>+}
>+
>+/**
>+ * dpll_pin_type_get - get type of a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @type: on success - configured pin type
>+ *
>+ * Return:
>+ * * 0 - successfully got pin's type
>+ * * negative - failed to get pin's type
>+ */
>+int dpll_pin_type_get(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin,
>+		      enum dpll_pin_type *type)
>+{
>+	if (!pin)
>+		return -ENODEV;
>+	*type = pin->type;
>+
>+	return 0;
>+}
>+
>+/**
>+ * dpll_pin_signal_type_get - get signal type of a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @type: on success - configured signal type
>+ *
>+ * Return:
>+ * * 0 - successfully got signal type
>+ * * negative - failed to obtain signal type
>+ */
>+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     enum dpll_pin_signal_type *type)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->signal_type_get)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->signal_type_get(ref->dpll, pin, type);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_signal_type_set - set signal type of a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @type: type to be set
>+ *
>+ * Return:
>+ * * 0 - signal type set
>+ * * negative - failed to set signal type
>+ */
>+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     const enum dpll_pin_signal_type type)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>+		if (!ref->dpll)
>+			return -EFAULT;
>+		if (!ref || !ref->ops || !ref->ops->signal_type_set)
>+			return -EOPNOTSUPP;
>+		if (ref->dpll != dpll)
>+			mutex_lock(&ref->dpll->lock);
>+		ret = ref->ops->signal_type_set(ref->dpll, pin, type);
>+		if (ref->dpll != dpll)
>+			mutex_unlock(&ref->dpll->lock);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_signal_type_supported - check if signal type is supported on a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @type: type being checked
>+ * @supported: on success - if given signal type is supported
>+ *
>+ * Return:
>+ * * 0 - successfully got supported signal type
>+ * * negative - failed to obtain supported signal type
>+ */
>+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>+				   const struct dpll_pin *pin,
>+				   const enum dpll_pin_signal_type type,
>+				   bool *supported)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->signal_type_supported)
>+		return -EOPNOTSUPP;
>+	*supported = ref->ops->signal_type_supported(ref->dpll, pin, type);
>+
>+	return 0;
>+}
>+
>+/**
>+ * dpll_pin_mode_active - check if given mode is active on a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @mode: mode being checked
>+ * @active: on success - if mode is active
>+ *
>+ * Return:
>+ * * 0 - successfully checked if mode is active
>+ * * negative - failed to check for active mode
>+ */
>+int dpll_pin_mode_active(const struct dpll_device *dpll,
>+			  const struct dpll_pin *pin,
>+			  const enum dpll_pin_mode mode,
>+			  bool *active)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->mode_active)
>+		return -EOPNOTSUPP;
>+	*active = ref->ops->mode_active(ref->dpll, pin, mode);
>+
>+	return 0;
>+}
>+
>+/**
>+ * dpll_pin_mode_supported - check if given mode is supported on a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @mode: mode being checked
>+ * @supported: on success - if mode is supported
>+ *
>+ * Return:
>+ * * 0 - successfully checked if mode is supported
>+ * * negative - failed to check for supported mode
>+ */
>+int dpll_pin_mode_supported(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     const enum dpll_pin_mode mode,
>+			     bool *supported)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->mode_supported)
>+		return -EOPNOTSUPP;
>+	*supported = ref->ops->mode_supported(ref->dpll, pin, mode);
>+
>+	return 0;
>+}
>+
>+/**
>+ * dpll_pin_mode_set - set pin's mode
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @mode: mode being set
>+ *
>+ * Return:
>+ * * 0 - successfully set the mode
>+ * * negative - failed to set the mode
>+ */
>+int dpll_pin_mode_set(const struct dpll_device *dpll,
>+		       const struct dpll_pin *pin,
>+		       const enum dpll_pin_mode mode)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {

I don't understand why you need to call ops->mode_enable for all the
dplls. The pin is shared, it's the single entity. One call should be
enough? Why not? Same for other attrs, with exception of PRIO.


>+		if (!ref)
>+			return -ENODEV;
>+		if (!ref->ops || !ref->ops->mode_enable)
>+			return -EOPNOTSUPP;
>+		if (ref->dpll != dpll)
>+			mutex_lock(&ref->dpll->lock);
>+		ret = ref->ops->mode_enable(ref->dpll, pin, mode);
>+		if (ref->dpll != dpll)
>+			mutex_unlock(&ref->dpll->lock);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_custom_freq_get - get pin's custom frequency
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @freq: on success - custom frequency of a pin
>+ *
>+ * Return:
>+ * * 0 - successfully got custom frequency
>+ * * negative - failed to obtain custom frequency
>+ */
>+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin, u32 *freq)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;

How this can happen?


>+	if (!ref->ops || !ref->ops->custom_freq_get)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->custom_freq_get(ref->dpll, pin, freq);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_custom_freq_set - set pin's custom frequency
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @freq: custom frequency to be set
>+ *
>+ * Return:
>+ * * 0 - successfully set custom frequency
>+ * * negative - failed to set custom frequency
>+ */
>+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin, const u32 freq)
>+{
>+	enum dpll_pin_signal_type type;
>+	struct dpll_pin_ref *ref;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>+		if (!ref)
>+			return -ENODEV;
>+		if (!ref->ops || !ref->ops->custom_freq_set ||
>+		    !ref->ops->signal_type_get)
>+			return -EOPNOTSUPP;
>+		if (dpll != ref->dpll)
>+			mutex_lock(&ref->dpll->lock);
>+		ret = ref->ops->signal_type_get(dpll, pin, &type);
>+		if (!ret && type == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ)
>+			ret = ref->ops->custom_freq_set(ref->dpll, pin, freq);
>+		if (dpll != ref->dpll)
>+			mutex_unlock(&ref->dpll->lock);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_prio_get - get pin's prio on dpll
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @prio: on success - priority of a pin on a dpll
>+ *
>+ * Return:
>+ * * 0 - successfully got priority
>+ * * negative - failed to obtain priority
>+ */
>+int dpll_pin_prio_get(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin, u32 *prio)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->prio_get)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->prio_get(ref->dpll, pin, prio);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_prio_set - set pin's prio on dpll
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @prio: priority of a pin to be set on a dpll
>+ *
>+ * Return:
>+ * * 0 - successfully set priority
>+ * * negative - failed to set the priority
>+ */
>+int dpll_pin_prio_set(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin, const u32 prio)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->prio_set)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->prio_set(ref->dpll, pin, prio);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_netifindex_get - get pin's netdev iterface index
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @netifindex: on success - index of a netdevice associated with pin
>+ *
>+ * Return:
>+ * * 0 - successfully got netdev interface index
>+ * * negative - failed to obtain netdev interface index
>+ */
>+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin,
>+			    int *netifindex)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->net_if_idx_get)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->net_if_idx_get(ref->dpll, pin, netifindex);

return right away.

>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_description - provide pin's description string
>+ * @pin: registered pin pointer
>+ *
>+ * Return: pointer to a description string.
>+ */
>+const char *dpll_pin_description(struct dpll_pin *pin)
>+{
>+	return pin->description;
>+}
>+
>+/**
>+ * dpll_pin_parent - provide pin's parent pin if available
>+ * @pin: registered pin pointer
>+ *
>+ * Return: pointer to aparent if found, NULL otherwise.
>+ */
>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)

What exactly is the reason of having one line helpers to access struct
fields for a struct which is known to the caller? Unneccesary
boilerplate code. Please remove these. For pin and for dpll_device as
well.



>+{
>+	return pin->parent_pin;
>+}
>+
>+/**
>+ * dpll_mode_set - handler for dpll mode set
>+ * @dpll: registered dpll pointer
>+ * @mode: mode to be set
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode)
>+{
>+	int ret;
>+
>+	if (!dpll->ops || !dpll->ops)
>+		return -EOPNOTSUPP;
>+
>+	ret = dpll->ops->mode_set(dpll, mode);

return right away. You have this pattern on multiple places, please fix.


>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_source_idx_set - handler for selecting a dpll's source
>+ * @dpll: registered dpll pointer
>+ * @source_pin_idx: index of a source pin to e selected
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_source_idx_set(struct dpll_device *dpll, const u32 source_pin_idx)
>+{
>+	struct dpll_pin_ref *ref;
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	pin = dpll_pin_get_by_idx_from_xa(&dpll->pins, source_pin_idx);
>+	if (!pin)
>+		return -ENXIO;
>+	ref = dpll_pin_find_ref(dpll, pin);
>+	if (!ref || !ref->ops)
>+		return -EFAULT;
>+	if (!ref->ops->select)
>+		return -ENODEV;

ENODEV definitelly does not look like the correct value here.


>+	ret = ref->ops->select(ref->dpll, pin);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_lock - locks the dpll using internal mutex
>+ * @dpll: registered dpll pointer
>+ */
>+void dpll_lock(struct dpll_device *dpll)
>+{
>+	mutex_lock(&dpll->lock);
>+}
>+
>+/**
>+ * dpll_unlock - unlocks the dpll using internal mutex
>+ * @dpll: registered dpll pointer
>+ */
>+void dpll_unlock(struct dpll_device *dpll)
>+{
>+	mutex_unlock(&dpll->lock);
>+}
>+
>+enum dpll_pin_type dpll_pin_type(const struct dpll_pin *pin)
>+{
>+	return pin->type;
>+}
>+
>+void *dpll_priv(const struct dpll_device *dpll)
>+{
>+	return dpll->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_priv);
>+
>+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return NULL;
>+
>+	return ref->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_priv);
>+
>+static int __init dpll_init(void)
>+{
>+	int ret;
>+
>+	ret = dpll_netlink_init();
>+	if (ret)
>+		goto error;
>+
>+	ret = class_register(&dpll_class);
>+	if (ret)
>+		goto unregister_netlink;
>+
>+	return 0;
>+
>+unregister_netlink:
>+	dpll_netlink_finish();
>+error:
>+	mutex_destroy(&dpll_device_xa_lock);
>+	return ret;
>+}
>+subsys_initcall(dpll_init);
>diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
>new file mode 100644
>index 000000000000..b933d63b60c1
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.h
>@@ -0,0 +1,105 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+#ifndef __DPLL_CORE_H__
>+#define __DPLL_CORE_H__
>+
>+#include <linux/dpll.h>
>+
>+#include "dpll_netlink.h"
>+
>+#define to_dpll_device(_dev) \
>+	container_of(_dev, struct dpll_device, dev)
>+
>+/**
>+ * struct dpll_device - structure for a DPLL device
>+ * @id:		unique id number for each device
>+ * @dev:	struct device for this dpll device
>+ * @parent:	parent device
>+ * @ops:	operations this &dpll_device supports
>+ * @lock:	mutex to serialize operations
>+ * @type:	type of a dpll
>+ * @priv:	pointer to private information of owner
>+ * @pins:	list of pointers to pins registered with this dpll
>+ * @clock_id:	unique identifier (clock_id) of a dpll
>+ * @clock_class	quality class of a DPLL clock
>+ * @dev_driver_idx: provided by driver for
>+ */
>+struct dpll_device {
>+	u32 id;
>+	struct device dev;
>+	struct device *parent;
>+	struct dpll_device_ops *ops;
>+	struct mutex lock;
>+	enum dpll_type type;
>+	void *priv;
>+	struct xarray pins;
>+	u64 clock_id;
>+	enum dpll_clock_class clock_class;
>+	u8 dev_driver_idx;
>+};
>+
>+#define for_each_pin_on_dpll(dpll, pin, i)			\
>+	for (pin = dpll_pin_first(dpll, &i); pin != NULL;	\
>+	     pin = dpll_pin_next(dpll, &i))

What is the purpose for this macro and dpll_pin_first/next() helper?
Why is this not equivalent to:
xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED)


>+
>+#define for_each_dpll(dpll, i)                         \
>+	for (dpll = dpll_first(&i); dpll != NULL; dpll = dpll_next(&i))

Same here, why this macro and helpers are needed?


>+
>+struct dpll_device *dpll_device_get_by_id(int id);
>+
>+struct dpll_device *dpll_device_get_by_name(const char *name);
>+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long *index);
>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index);
>+struct dpll_device *dpll_first(unsigned long *index);
>+struct dpll_device *dpll_next(unsigned long *index);
>+void dpll_device_unregister(struct dpll_device *dpll);
>+u32 dpll_id(struct dpll_device *dpll);
>+const char *dpll_dev_name(struct dpll_device *dpll);
>+void dpll_lock(struct dpll_device *dpll);
>+void dpll_unlock(struct dpll_device *dpll);
>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>+int dpll_pin_type_get(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin,
>+		      enum dpll_pin_type *type);
>+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     enum dpll_pin_signal_type *type);
>+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     const enum dpll_pin_signal_type type);
>+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>+				   const struct dpll_pin *pin,
>+				   const enum dpll_pin_signal_type type,
>+				   bool *supported);
>+int dpll_pin_mode_active(const struct dpll_device *dpll,
>+			 const struct dpll_pin *pin,
>+			 const enum dpll_pin_mode mode,
>+			 bool *active);
>+int dpll_pin_mode_supported(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin,
>+			    const enum dpll_pin_mode mode,
>+			    bool *supported);
>+int dpll_pin_mode_set(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin,
>+		      const enum dpll_pin_mode mode);
>+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin, u32 *freq);
>+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin, const u32 freq);
>+int dpll_pin_prio_get(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin, u32 *prio);
>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx);
>+int dpll_pin_prio_set(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin, const u32 prio);
>+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin,
>+			    int *netifindex);
>+const char *dpll_pin_description(struct dpll_pin *pin);
>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin);
>+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode);
>+int dpll_source_idx_set(struct dpll_device *dpll, const u32 source_pin_idx);
>+
>+#endif
>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>new file mode 100644
>index 000000000000..91a1e5025ab2
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -0,0 +1,883 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Generic netlink for DPLL management framework
>+ *
>+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ *
>+ */
>+#include <linux/module.h>
>+#include <linux/kernel.h>
>+#include <net/genetlink.h>
>+#include "dpll_core.h"
>+
>+#include <uapi/linux/dpll.h>
>+
>+static const struct genl_multicast_group dpll_mcgrps[] = {
>+	{ .name = DPLL_MONITOR_GROUP_NAME,  },
>+};
>+
>+static const struct nla_policy dpll_cmd_device_get_policy[] = {
>+	[DPLLA_ID]		= { .type = NLA_U32 },
>+	[DPLLA_NAME]		= { .type = NLA_STRING,
>+				    .len = DPLL_NAME_LEN },
>+	[DPLLA_FILTER]		= { .type = NLA_U32 },
>+};
>+
>+static const struct nla_policy dpll_cmd_device_set_policy[] = {
>+	[DPLLA_ID]		= { .type = NLA_U32 },
>+	[DPLLA_NAME]		= { .type = NLA_STRING,
>+				    .len = DPLL_NAME_LEN },
>+	[DPLLA_MODE]		= { .type = NLA_U32 },
>+	[DPLLA_SOURCE_PIN_IDX]	= { .type = NLA_U32 },
>+};
>+
>+static const struct nla_policy dpll_cmd_pin_set_policy[] = {
>+	[DPLLA_ID]		= { .type = NLA_U32 },
>+	[DPLLA_PIN_IDX]		= { .type = NLA_U32 },
>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>+	[DPLLA_PIN_MODE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>+};
>+
>+struct dpll_dump_ctx {
>+	int dump_filter;
>+};
>+
>+static struct genl_family dpll_gnl_family;
>+
>+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
>+{
>+	return (struct dpll_dump_ctx *)cb->ctx;
>+}
>+
>+static int dpll_msg_add_id(struct sk_buff *msg, u32 id)
>+{
>+	if (nla_put_u32(msg, DPLLA_ID, id))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_name(struct sk_buff *msg, const char *name)
>+{
>+	if (nla_put_string(msg, DPLLA_NAME, name))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int __dpll_msg_add_mode(struct sk_buff *msg, enum dplla msg_type,
>+			       enum dpll_mode mode)
>+{
>+	if (nla_put_s32(msg, msg_type, mode))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll)
>+{
>+	enum dpll_mode m;
>+	int ret;
>+
>+	if (!dpll->ops->mode_get)
>+		return 0;
>+	ret = dpll->ops->mode_get(dpll, &m);
>+	if (ret)
>+		return ret;
>+
>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>+}
>+
>+static int
>+dpll_msg_add_modes_supported(struct sk_buff *msg,
>+			     const struct dpll_device *dpll)
>+{
>+	enum dpll_mode i;
>+	int ret = 0;
>+
>+	if (!dpll->ops->mode_supported)
>+		return ret;
>+
>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>+		if (dpll->ops->mode_supported(dpll, i)) {
>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_msg_add_clock_id(struct sk_buff *msg,
>+				 const struct dpll_device *dpll)
>+{
>+	if (nla_put_64bit(msg, DPLLA_CLOCK_ID, sizeof(dpll->clock_id),
>+			  &dpll->clock_id, 0))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_clock_class(struct sk_buff *msg,
>+				    const struct dpll_device *dpll)
>+{
>+	if (nla_put_s32(msg, DPLLA_CLOCK_CLASS, dpll->clock_class))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	u32 source_idx;
>+	int ret;
>+
>+	if (!dpll->ops->source_pin_idx_get)
>+		return 0;
>+	ret = dpll->ops->source_pin_idx_get(dpll, &source_idx);
>+	if (ret)
>+		return ret;
>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	enum dpll_lock_status s;
>+	int ret;
>+
>+	if (!dpll->ops->lock_status_get)
>+		return 0;
>+	ret = dpll->ops->lock_status_get(dpll, &s);
>+	if (ret)
>+		return ret;
>+	if (nla_put_s32(msg, DPLLA_LOCK_STATUS, s))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	s32 temp;
>+	int ret;
>+
>+	if (!dpll->ops->temp_get)
>+		return 0;
>+	ret = dpll->ops->temp_get(dpll, &temp);
>+	if (ret)
>+		return ret;
>+	if (nla_put_u32(msg, DPLLA_TEMP, temp))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_idx(struct sk_buff *msg, u32 pin_idx)
>+{
>+	if (nla_put_u32(msg, DPLLA_PIN_IDX, pin_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_description(struct sk_buff *msg,
>+					const char *description)
>+{
>+	if (nla_put_string(msg, DPLLA_PIN_DESCRIPTION, description))

I don't understand the reason to have these helpers. I said that before,
just call nla_put_* directly and avoid these unnecessary boilerplate
unctions.


>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_parent_idx(struct sk_buff *msg, u32 parent_idx)
>+{
>+	if (nla_put_u32(msg, DPLLA_PIN_PARENT_IDX, parent_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin)
>+{
>+	enum dpll_pin_type t;
>+
>+	if (dpll_pin_type_get(dpll, pin, &t))
>+		return 0;
>+
>+	if (nla_put_s32(msg, DPLLA_PIN_TYPE, t))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int __dpll_msg_add_pin_signal_type(struct sk_buff *msg,
>+					  enum dplla attr,
>+					  enum dpll_pin_signal_type type)
>+{
>+	if (nla_put_s32(msg, attr, type))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_signal_type(struct sk_buff *msg,
>+					const struct dpll_device *dpll,
>+					const struct dpll_pin *pin)
>+{
>+	enum dpll_pin_signal_type t;

s/t/type/


>+	int ret;
>+
>+	if (dpll_pin_signal_type_get(dpll, pin, &t))

Why don't you propagate the error value?


>+		return 0;
>+	ret = __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>+	if (ret)
>+		return ret;
>+
>+	if (t == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) {
>+		u32 freq;
>+
>+		if (dpll_pin_custom_freq_get(dpll, pin, &freq))
>+			return 0;
>+		if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>+			return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>+					const struct dpll_device *dpll,
>+					const struct dpll_pin *pin)
>+{
>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>+	enum dpll_pin_signal_type i;
>+	bool supported;
>+
>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>+		if (dpll_pin_signal_type_supported(dpll, pin, i, &supported))
>+			continue;
>+		if (supported) {
>+			int ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>+
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>+				   const struct dpll_device *dpll,
>+				   const struct dpll_pin *pin)
>+{
>+	enum dpll_pin_mode i;
>+	bool active;
>+
>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>+			return 0;
>+		if (active)
>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))

Why this is signed?


>+				return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_modes_supported(struct sk_buff *msg,
>+					     const struct dpll_device *dpll,
>+					     const struct dpll_pin *pin)
>+{
>+	enum dpll_pin_mode i;
>+	bool supported;
>+
>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>+		if (dpll_pin_mode_supported(dpll, pin, i, &supported))
>+			return 0;
>+		if (supported)
>+			if (nla_put_s32(msg, DPLLA_PIN_MODE_SUPPORTED, i))

Here too. Please check the rest, you should not need to put signed
values.


>+				return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin)
>+{
>+	u32 prio;
>+
>+	if (dpll_pin_prio_get(dpll, pin, &prio))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_PIN_PRIO, prio))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin)
>+{
>+	int netifindex;
>+
>+	if (dpll_pin_netifindex_get(dpll, pin, &netifindex))
>+		return 0;
>+	if (nla_put_s32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+__dpll_cmd_device_dump_one(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
>+
>+	return ret;
>+}
>+
>+static int
>+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_device *dpll,
>+			struct dpll_pin *pin)
>+{
>+	struct dpll_pin *parent = NULL;
>+	int ret;
>+
>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_description(msg, dpll_pin_description(pin));
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_type(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	parent = dpll_pin_parent(pin);
>+	if (parent) {
>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>+								    parent));
>+		if (ret)
>+			return ret;
>+	}
>+	ret = dpll_msg_add_pin_signal_type(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_signal_types_supported(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_modes(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_modes_supported(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_prio(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_netifindex(msg, dpll, pin);
>+
>+	return ret;
>+}
>+
>+static int __dpll_cmd_dump_pins(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	struct dpll_pin *pin;
>+	struct nlattr *attr;
>+	unsigned long i;
>+	int ret = 0;
>+
>+	for_each_pin_on_dpll(dpll, pin, i) {
>+		attr = nla_nest_start(msg, DPLLA_PIN);
>+		if (!attr) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		ret = __dpll_cmd_pin_dump_one(msg, dpll, pin);
>+		if (ret)
>+			goto nest_cancel;
>+		nla_nest_end(msg, attr);
>+	}
>+
>+	return ret;
>+
>+nest_cancel:
>+	nla_nest_cancel(msg, attr);
>+	return ret;
>+}
>+
>+static int
>+__dpll_cmd_dump_status(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	int ret = dpll_msg_add_source_pin(msg, dpll);
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_temp(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_lock_status(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_mode(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_modes_supported(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_clock_id(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_clock_class(msg, dpll);
>+
>+	return ret;
>+}
>+
>+static int
>+dpll_device_dump_one(struct dpll_device *dpll, struct sk_buff *msg,
>+		     int dump_filter)
>+{
>+	int ret;
>+
>+	dpll_lock(dpll);
>+	ret = __dpll_cmd_device_dump_one(msg, dpll);
>+	if (ret)
>+		goto out_unlock;
>+
>+	if (dump_filter & DPLL_FILTER_STATUS) {
>+		ret = __dpll_cmd_dump_status(msg, dpll);
>+		if (ret) {
>+			if (ret != -EMSGSIZE)
>+				ret = -EAGAIN;
>+			goto out_unlock;
>+		}
>+	}
>+	if (dump_filter & DPLL_FILTER_PINS)
>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>+	dpll_unlock(dpll);
>+
>+	return ret;
>+out_unlock:
>+	dpll_unlock(dpll);
>+	return ret;
>+}
>+
>+static int
>+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
>+			 struct dpll_pin *pin, struct genl_info *info)
>+{
>+	enum dpll_pin_signal_type st;
>+	enum dpll_pin_mode mode;
>+	struct nlattr *a;
>+	int rem, ret = 0;
>+	u32 prio, freq;
>+
>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>+			  genlmsg_len(info->genlhdr), rem) {
>+		switch (nla_type(a)) {
>+		case DPLLA_PIN_SIGNAL_TYPE:
>+			st = nla_get_s32(a);
>+			ret = dpll_pin_signal_type_set(dpll, pin, st);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_CUSTOM_FREQ:
>+			freq = nla_get_u32(a);
>+			ret = dpll_pin_custom_freq_set(dpll, pin, freq);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_MODE:
>+			mode = nla_get_s32(a);
>+			ret = dpll_pin_mode_set(dpll, pin, mode);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_PRIO:
>+			prio = nla_get_u32(a);
>+			ret = dpll_pin_prio_set(dpll, pin, prio);
>+			if (ret)
>+				return ret;
>+			break;
>+		default:
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_cmd_pin_set(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	struct nlattr **attrs = info->attrs;
>+	struct dpll_pin *pin;
>+	int pin_id;
>+
>+	if (!attrs[DPLLA_PIN_IDX])
>+		return -EINVAL;
>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>+	dpll_lock(dpll);
>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>+	dpll_unlock(dpll);
>+	if (!pin)
>+		return -ENODEV;
>+	return dpll_pin_set_from_nlattr(dpll, pin, info);
>+}
>+
>+enum dpll_mode dpll_msg_read_mode(struct nlattr *a)
>+{
>+	return nla_get_s32(a);
>+}
>+
>+u32 dpll_msg_read_source_pin_id(struct nlattr *a)
>+{
>+	return nla_get_u32(a);
>+}
>+
>+static int
>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>+{
>+	enum dpll_mode m;
>+	struct nlattr *a;
>+	int rem, ret = 0;
>+	u32 source_pin;
>+
>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>+			  genlmsg_len(info->genlhdr), rem) {
>+		switch (nla_type(a)) {
>+		case DPLLA_MODE:
>+			m = dpll_msg_read_mode(a);
>+
>+			ret = dpll_mode_set(dpll, m);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_SOURCE_PIN_IDX:
>+			source_pin = dpll_msg_read_source_pin_id(a);
>+
>+			ret = dpll_source_idx_set(dpll, source_pin);
>+			if (ret)
>+				return ret;
>+			break;
>+		default:
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_cmd_device_set(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_device *dpll = info->user_ptr[0];
>+
>+	return dpll_set_from_nlattr(dpll, info);
>+}
>+
>+static int
>+dpll_cmd_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>+	struct dpll_device *dpll;
>+	struct nlattr *hdr;
>+	unsigned long i;
>+	int ret;
>+
>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
>+			  &dpll_gnl_family, 0, DPLL_CMD_DEVICE_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	for_each_dpll(dpll, i) {
>+		ret = dpll_device_dump_one(dpll, skb, ctx->dump_filter);
>+		if (ret)
>+			break;
>+	}
>+
>+	if (ret)
>+		genlmsg_cancel(skb, hdr);
>+	else
>+		genlmsg_end(skb, hdr);
>+
>+	return ret;
>+}
>+
>+static int dpll_cmd_device_get(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	struct nlattr **attrs = info->attrs;
>+	struct sk_buff *msg;
>+	int dump_filter = 0;
>+	struct nlattr *hdr;
>+	int ret;
>+
>+	if (attrs[DPLLA_FILTER])
>+		dump_filter = nla_get_s32(attrs[DPLLA_FILTER]);
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+	hdr = genlmsg_put_reply(msg, info, &dpll_gnl_family, 0,
>+				DPLL_CMD_DEVICE_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	ret = dpll_device_dump_one(dpll, msg, dump_filter);
>+	if (ret)
>+		goto out_free_msg;
>+	genlmsg_end(msg, hdr);
>+
>+	return genlmsg_reply(msg, info);
>+
>+out_free_msg:
>+	nlmsg_free(msg);
>+	return ret;
>+
>+}
>+
>+static int dpll_cmd_device_get_start(struct netlink_callback *cb)
>+{
>+	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
>+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>+	struct nlattr *attr = info->attrs[DPLLA_FILTER];
>+
>+	if (attr)
>+		ctx->dump_filter = nla_get_s32(attr);
>+	else
>+		ctx->dump_filter = 0;
>+
>+	return 0;
>+}
>+
>+static int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+			 struct genl_info *info)
>+{
>+	struct dpll_device *dpll_id = NULL, *dpll_name = NULL;
>+
>+	if (!info->attrs[DPLLA_ID] &&
>+	    !info->attrs[DPLLA_NAME])
>+		return -EINVAL;
>+
>+	if (info->attrs[DPLLA_ID]) {
>+		u32 id = nla_get_u32(info->attrs[DPLLA_ID]);
>+
>+		dpll_id = dpll_device_get_by_id(id);
>+		if (!dpll_id)
>+			return -ENODEV;
>+		info->user_ptr[0] = dpll_id;
>+	}
>+	if (info->attrs[DPLLA_NAME]) {
>+		const char *name = nla_data(info->attrs[DPLLA_NAME]);
>+
>+		dpll_name = dpll_device_get_by_name(name);
>+		if (!dpll_name)
>+			return -ENODEV;
>+
>+		if (dpll_id && dpll_name != dpll_id)
>+			return -EINVAL;
>+		info->user_ptr[0] = dpll_name;
>+	}
>+
>+	return 0;
>+}
>+
>+static const struct genl_ops dpll_ops[] = {
>+	{
>+		.cmd	= DPLL_CMD_DEVICE_GET,
>+		.flags  = GENL_UNS_ADMIN_PERM,
>+		.start	= dpll_cmd_device_get_start,
>+		.dumpit	= dpll_cmd_device_dump,
>+		.doit	= dpll_cmd_device_get,
>+		.policy	= dpll_cmd_device_get_policy,
>+		.maxattr = ARRAY_SIZE(dpll_cmd_device_get_policy) - 1,
>+	},
>+	{
>+		.cmd	= DPLL_CMD_DEVICE_SET,
>+		.flags	= GENL_UNS_ADMIN_PERM,
>+		.doit	= dpll_cmd_device_set,
>+		.policy	= dpll_cmd_device_set_policy,
>+		.maxattr = ARRAY_SIZE(dpll_cmd_device_set_policy) - 1,
>+	},
>+	{
>+		.cmd	= DPLL_CMD_PIN_SET,
>+		.flags	= GENL_UNS_ADMIN_PERM,
>+		.doit	= dpll_cmd_pin_set,
>+		.policy	= dpll_cmd_pin_set_policy,
>+		.maxattr = ARRAY_SIZE(dpll_cmd_pin_set_policy) - 1,
>+	},
>+};
>+
>+static struct genl_family dpll_family __ro_after_init = {
>+	.hdrsize	= 0,

No need for static.


>+	.name		= DPLL_FAMILY_NAME,
>+	.version	= DPLL_VERSION,
>+	.ops		= dpll_ops,
>+	.n_ops		= ARRAY_SIZE(dpll_ops),
>+	.mcgrps		= dpll_mcgrps,
>+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
>+	.pre_doit	= dpll_pre_doit,
>+	.parallel_ops   = true,
>+};
>+
>+static int dpll_event_device_id(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
>+
>+	return ret;
>+}
>+
>+static int dpll_event_device_change(struct sk_buff *msg,
>+				    struct dpll_device *dpll,
>+				    struct dpll_pin *pin,
>+				    enum dpll_event_change event)
>+{
>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = nla_put_s32(msg, DPLLA_CHANGE_TYPE, event);
>+	if (ret)
>+		return ret;
>+	switch (event)	{
>+	case DPLL_CHANGE_PIN_ADD:
>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>+	case DPLL_CHANGE_PIN_MODE:
>+	case DPLL_CHANGE_PIN_PRIO:
>+		ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>+		break;
>+	default:
>+		break;
>+	}
>+
>+	return ret;
>+}
>+
>+/*
>+ * Generic netlink DPLL event encoding
>+ */
>+static int dpll_send_event_create(enum dpll_event event,
>+				  struct dpll_device *dpll)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+
>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>+	if (!hdr)
>+		goto out_free_msg;
>+
>+	ret = dpll_event_device_id(msg, dpll);
>+	if (ret)
>+		goto out_cancel_msg;
>+	genlmsg_end(msg, hdr);
>+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
>+
>+	return 0;
>+
>+out_cancel_msg:
>+	genlmsg_cancel(msg, hdr);
>+out_free_msg:
>+	nlmsg_free(msg);
>+
>+	return ret;
>+}
>+
>+/*
>+ * Generic netlink DPLL event encoding
>+ */
>+static int dpll_send_event_change(struct dpll_device *dpll,
>+				  struct dpll_pin *pin,
>+				  enum dpll_event_change event)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+
>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, DPLL_EVENT_DEVICE_CHANGE);
>+	if (!hdr)
>+		goto out_free_msg;
>+
>+	ret = dpll_event_device_change(msg, dpll, pin, event);
>+	if (ret)
>+		goto out_cancel_msg;
>+	genlmsg_end(msg, hdr);
>+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
>+
>+	return 0;
>+
>+out_cancel_msg:
>+	genlmsg_cancel(msg, hdr);
>+out_free_msg:
>+	nlmsg_free(msg);
>+
>+	return ret;
>+}
>+
>+int dpll_notify_device_create(struct dpll_device *dpll)
>+{
>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
>+}
>+
>+int dpll_notify_device_delete(struct dpll_device *dpll)
>+{
>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
>+}
>+
>+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change event)
>+{
>+	return dpll_send_event_change(dpll, NULL, event);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_notify);
>+
>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dpll_event_change event)
>+{
>+	return dpll_send_event_change(dpll, pin, event);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_notify);
>+
>+int __init dpll_netlink_init(void)
>+{
>+	return genl_register_family(&dpll_family);
>+}
>+
>+void dpll_netlink_finish(void)
>+{
>+	genl_unregister_family(&dpll_family);
>+}
>+
>+void __exit dpll_netlink_fini(void)
>+{
>+	dpll_netlink_finish();
>+}
>diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
>new file mode 100644
>index 000000000000..8e50b2493027
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.h
>@@ -0,0 +1,24 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+/**
>+ * dpll_notify_device_create - notify that the device has been created
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_notify_device_create(struct dpll_device *dpll);
>+
>+
>+/**
>+ * dpll_notify_device_delete - notify that the device has been deleted
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_notify_device_delete(struct dpll_device *dpll);
>+
>+int __init dpll_netlink_init(void);
>+void dpll_netlink_finish(void);
>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>new file mode 100644
>index 000000000000..fcba46ea1b7b
>--- /dev/null
>+++ b/include/linux/dpll.h
>@@ -0,0 +1,282 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+#ifndef __DPLL_H__
>+#define __DPLL_H__
>+
>+#include <uapi/linux/dpll.h>
>+#include <linux/device.h>
>+
>+struct dpll_device;
>+struct dpll_pin;
>+
>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>+
>+struct dpll_device_ops {
>+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode);
>+	int (*mode_set)(const struct dpll_device *dpll,
>+			const enum dpll_mode mode);
>+	bool (*mode_supported)(const struct dpll_device *dpll,
>+			       const enum dpll_mode mode);
>+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
>+				  u32 *pin_idx);
>+	int (*lock_status_get)(const struct dpll_device *dpll,
>+			       enum dpll_lock_status *status);
>+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp);
>+};
>+
>+struct dpll_pin_ops {
>+	int (*signal_type_get)(const struct dpll_device *dpll,
>+			       const struct dpll_pin *pin,
>+			       enum dpll_pin_signal_type *type);
>+	int (*signal_type_set)(const struct dpll_device *dpll,
>+			       const struct dpll_pin *pin,
>+			       const enum dpll_pin_signal_type type);
>+	bool (*signal_type_supported)(const struct dpll_device *dpll,
>+				      const struct dpll_pin *pin,
>+				      const enum dpll_pin_signal_type type);
>+	int (*custom_freq_set)(const struct dpll_device *dpll,
>+			       const struct dpll_pin *pin,
>+			       const u32 custom_freq);
>+	int (*custom_freq_get)(const struct dpll_device *dpll,
>+			       const struct dpll_pin *pin,
>+			       u32 *custom_freq);
>+	bool (*mode_active)(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     const enum dpll_pin_mode mode);
>+	int (*mode_enable)(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin,
>+			    const enum dpll_pin_mode mode);
>+	bool (*mode_supported)(const struct dpll_device *dpll,
>+				const struct dpll_pin *pin,
>+				const enum dpll_pin_mode mode);
>+	int (*prio_get)(const struct dpll_device *dpll,
>+			const struct dpll_pin *pin,
>+			u32 *prio);
>+	int (*prio_set)(const struct dpll_device *dpll,
>+			const struct dpll_pin *pin,
>+			const u32 prio);
>+	int (*net_if_idx_get)(const struct dpll_device *dpll,
>+			      const struct dpll_pin *pin,
>+			      int *net_if_idx);
>+	int (*select)(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin);

Could you please pass extack to all of the ops? I think it is important
to give the user the meaningfull error message from the start.


>+};
>+
>+/**
>+ * dpll_device_alloc - allocate memory for a new dpll_device object
>+ * @ops: pointer to dpll operations structure
>+ * @type: type of a dpll being allocated
>+ * @clock_id: a system unique number for a device
>+ * @clock_class: quality class of a DPLL clock
>+ * @dev_driver_idx: index of dpll device on parent device
>+ * @priv: private data of a registerer
>+ * @parent: device structure of a module registering dpll device
>+ *
>+ * Allocate memory for a new dpll and initialize it with its type, name,
>+ * callbacks and private data pointer.
>+ *
>+ * Name is generated based on: parent driver, type and dev_driver_idx.
>+ * Finding allocated and registered dpll device is also possible with
>+ * the: clock_id, type and dev_driver_idx. This way dpll device can be
>+ * shared by multiple instances of a device driver.
>+ *
>+ * Returns:
>+ * * pointer to initialized dpll - success
>+ * * NULL - memory allocation fail
>+ */
>+struct dpll_device
>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>+		   u8 dev_driver_idx, void *priv, struct device *parent);
>+
>+/**
>+ * dpll_device_unregister - unregister registered dpll
>+ * @dpll: pointer to dpll
>+ *
>+ * Unregister the dpll from the subsystem, make it unavailable for netlink
>+ * API users.
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_device_free - free dpll memory
>+ * @dpll: pointer to dpll
>+ *
>+ * Free memory allocated with ``dpll_device_alloc(..)``
>+ */
>+void dpll_device_free(struct dpll_device *dpll);


Could you please sort the functions? I mean, dpll_device_unregister() in
currently in the middle of dpll_device_alloc() and dpll_device_free()

Also, there is no dpll_device_register(), that is odd.


>+
>+/**
>+ * dpll_priv - get private data
>+ * @dpll: pointer to dpll
>+ *
>+ * Obtain private data pointer passed to dpll subsystem when allocating
>+ * device with ``dpll_device_alloc(..)``
>+ */
>+void *dpll_priv(const struct dpll_device *dpll);
>+
>+/**
>+ * dpll_pin_priv - get private data
>+ * @dpll: pointer to dpll
>+ *
>+ * Obtain private pin data pointer passed to dpll subsystem when pin
>+ * was registered with dpll.
>+ */
>+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_idx - get pin idx
>+ * @dpll: pointer to dpll
>+ * @pin: pointer to a pin
>+ *
>+ * Obtain pin index of given pin on given dpll.
>+ *
>+ * Return: PIN_IDX_INVALID - if failed to find pin, otherwise pin index
>+ */
>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);

You don't use this in driver (and I don't see any need for that). Remove
from the public header.


>+
>+/**
>+ * dpll_shared_pin_register - share a pin between dpll devices
>+ * @dpll_pin_owner: a dpll already registered with a pin
>+ * @dpll: a dpll being registered with a pin
>+ * @shared_pin_description: identifies pin registered with dpll device
>+ *	(@dpll_pin_owner) which is now being registered with new dpll (@dpll)
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ *
>+ * Register a pin already registered with different dpll device.
>+ * Allow to share a single pin within multiple dpll instances.
>+ *
>+ * Returns:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+int
>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>+			 struct dpll_device *dpll,
>+			 const char *shared_pin_description,
>+			 struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_pin_alloc - allocate memory for a new dpll_pin object
>+ * @description: pointer to string description of a pin with max length
>+ * equal to PIN_DESC_LEN
>+ * @type: type of allocated pin
>+ *
>+ * Allocate memory for a new pin and initialize its resources.
>+ *
>+ * Returns:
>+ * * pointer to initialized pin - success
>+ * * NULL - memory allocation fail
>+ */
>+struct dpll_pin *dpll_pin_alloc(const char *description,
>+				const enum dpll_pin_type type);
>+
>+/**
>+ * dpll_pin_register - register pin with a dpll device
>+ * @dpll: pointer to dpll object to register pin with
>+ * @pin: pointer to allocated pin object being registered with dpll
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ *
>+ * Register previously allocated pin object with a dpll device.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this dpll,
>+ * * -EBUSY - couldn't allocate id for a pin.
>+ */
>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_pin_deregister - deregister pin from a dpll device
>+ * @dpll: pointer to dpll object to deregister pin from
>+ * @pin: pointer to allocated pin object being deregistered from dpll
>+ *
>+ * Deregister previously registered pin object from a dpll device.
>+ *
>+ * Return:
>+ * * 0 - pin was successfully deregistered from this dpll device,
>+ * * -ENXIO - given pin was not registered with this dpll device,
>+ * * -EINVAL - pin pointer is not valid.
>+ */
>+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_free - free memory allocated for a pin
>+ * @pin: pointer to allocated pin object being freed
>+ *
>+ * Shared pins must be deregistered from all dpll devices before freeing them,
>+ * otherwise the memory won't be freed.
>+ */
>+void dpll_pin_free(struct dpll_pin *pin);
>+
>+/**
>+ * dpll_muxed_pin_register - register a pin to a muxed-type pin
>+ * @parent_pin_description: parent pin description as given on it's allocation
>+ * @pin: pointer to allocated pin object being registered with a parent pin
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops*
>+ *
>+ * In case of multiplexed pins, allows registring them under a single
>+ * parent pin.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this parent pin,
>+ * * -EBUSY - couldn't assign id for a pin.
>+ */
>+int dpll_muxed_pin_register(struct dpll_device *dpll,
>+			    const char *parent_pin_description,
>+			    struct dpll_pin *pin,
>+			    struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_device_get_by_clock_id - find a dpll by its clock_id, type and index
>+ * @clock_id: clock_id of dpll, as given by driver on ``dpll_device_alloc``
>+ * @type: type of dpll, as given by driver on ``dpll_device_alloc``
>+ * @idx: index of dpll, as given by driver on ``dpll_device_alloc``
>+ *
>+ * Allows multiple driver instances using one physical DPLL to find
>+ * and share already registered DPLL device.
>+ *
>+ * Return: pointer if device was found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>+						enum dpll_type type, u8 idx);
>+
>+/**
>+ * dpll_device_notify - notify on dpll device change
>+ * @dpll: dpll device pointer
>+ * @event: type of change
>+ *
>+ * Broadcast event to the netlink multicast registered listeners.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change event);
>+
>+/**
>+ * dpll_pin_notify - notify on dpll pin change
>+ * @dpll: dpll device pointer
>+ * @pin: dpll pin pointer
>+ * @event: type of change
>+ *
>+ * Broadcast event to the netlink multicast registered listeners.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dpll_event_change event);

You don't use this from driver, remove it from the public header.


>+
>+#endif
>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>new file mode 100644
>index 000000000000..b7dbdd814b5c
>--- /dev/null
>+++ b/include/uapi/linux/dpll.h
>@@ -0,0 +1,294 @@
>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>+#ifndef _UAPI_LINUX_DPLL_H
>+#define _UAPI_LINUX_DPLL_H
>+
>+#define DPLL_NAME_LEN		32
>+#define DPLL_DESC_LEN		20
>+#define DPLL_PIN_DESC_LEN	20

I don't see why to limit this. Those strings are read only. See
DEVLINK_ATTR_BUS_NAME for example.


>+
>+/* Adding event notification support elements */
>+#define DPLL_FAMILY_NAME	"dpll"
>+#define DPLL_VERSION		0x01
>+#define DPLL_MONITOR_GROUP_NAME	"monitor"
>+
>+#define DPLL_FILTER_PINS	1
>+#define DPLL_FILTER_STATUS	2

Why again do we need any filtering here?


>+
>+/* dplla - Attributes of dpll generic netlink family
>+ *
>+ * @DPLLA_UNSPEC - invalid attribute
>+ * @DPLLA_ID - ID of a dpll device (unsigned int)
>+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH size)
>+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
>+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum dpll_mode)
>+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll

IDX


>+ *	(unsigned int)
>+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
>+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)

Hmm, wouldn't it be better to have it as 1/10 of Celsius degree for
example?


>+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
>+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum dpll_clock_class)
>+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests (int,
>+ *	sum of DPLL_DUMP_FILTER_* defines)
>+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
>+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
>+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by driver
>+ *	(char array of PIN_DESC_LEN size)
>+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
>+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
>+ *	(enum dpll_pin_signal_type)
>+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
>+ *	(enum dpll_pin_signal_type)
>+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
>+ *	(unsigned int)
>+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
>+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
>+ *	(enum dpll_pin_mode)
>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>+ * @DPLLA_CHANGE_TYPE - type of device change event
>+ *	(enum dpll_change_type)
>+ **/
>+enum dplla {
>+	DPLLA_UNSPEC,
>+	DPLLA_ID,
>+	DPLLA_NAME,
>+	DPLLA_MODE,
>+	DPLLA_MODE_SUPPORTED,
>+	DPLLA_SOURCE_PIN_IDX,
>+	DPLLA_LOCK_STATUS,
>+	DPLLA_TEMP,
>+	DPLLA_CLOCK_ID,
>+	DPLLA_CLOCK_CLASS,
>+	DPLLA_FILTER,
>+	DPLLA_PIN,
>+	DPLLA_PIN_IDX,
>+	DPLLA_PIN_DESCRIPTION,
>+	DPLLA_PIN_TYPE,
>+	DPLLA_PIN_SIGNAL_TYPE,
>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>+	DPLLA_PIN_CUSTOM_FREQ,
>+	DPLLA_PIN_MODE,
>+	DPLLA_PIN_MODE_SUPPORTED,
>+	DPLLA_PIN_PRIO,
>+	DPLLA_PIN_PARENT_IDX,
>+	DPLLA_PIN_NETIFINDEX,

I believe we cannot have this right now. The problem is, ifindexes may
overlay between namespaces. So ifindex without namespace means nothing.
I don't see how this can work from the dpll side.

Lets assign dpll_pin pointer to netdev and expose it over RT netlink in
a similar way devlink_port is exposed. That should be enough for the
user to find a dpll instance for given netdev.

It does not have to be part of this set strictly, but I would like to
have it here, so the full picture could be seen.



>+	DPLLA_CHANGE_TYPE,
>+	__DPLLA_MAX,
>+};
>+
>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>+
>+/* dpll_lock_status - DPLL status provides information of device status
>+ *
>+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
>+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid (or is in
>+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
>+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid signal
>+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
>+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a valid lock
>+ *	or was forced by DPLL_MODE_HOLDOVER mode)
>+ **/
>+enum dpll_lock_status {
>+	DPLL_LOCK_STATUS_UNSPEC,
>+	DPLL_LOCK_STATUS_UNLOCKED,
>+	DPLL_LOCK_STATUS_CALIBRATING,
>+	DPLL_LOCK_STATUS_LOCKED,
>+	DPLL_LOCK_STATUS_HOLDOVER,
>+
>+	__DPLL_LOCK_STATUS_MAX,
>+};
>+
>+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
>+
>+/* dpll_pin_type - signal types
>+ *
>+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
>+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
>+ * @DPLL_PIN_TYPE_EXT - external source
>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
>+ **/
>+enum dpll_pin_type {
>+	DPLL_PIN_TYPE_UNSPEC,
>+	DPLL_PIN_TYPE_MUX,
>+	DPLL_PIN_TYPE_EXT,
>+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>+	DPLL_PIN_TYPE_INT_OSCILLATOR,
>+	DPLL_PIN_TYPE_GNSS,
>+
>+	__DPLL_PIN_TYPE_MAX,
>+};
>+
>+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
>+
>+/* dpll_pin_signal_type - signal types
>+ *
>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal

Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
with HZ value directly? For example, supported freq:
1, 10000000
or:
1, 1000

freq set 10000000
freq set 1

Simple and easy.


>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value defined
>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>+ **/
>+enum dpll_pin_signal_type {
>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>+
>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>+};
>+
>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>+
>+/* dpll_pin_mode - available pin states
>+ *
>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>+ **/
>+enum dpll_pin_mode {
>+	DPLL_PIN_MODE_UNSPEC,
>+	DPLL_PIN_MODE_CONNECTED,
>+	DPLL_PIN_MODE_DISCONNECTED,
>+	DPLL_PIN_MODE_SOURCE,
>+	DPLL_PIN_MODE_OUTPUT,

I don't follow. I see 2 enums:
CONNECTED/DISCONNECTED
SOURCE/OUTPUT
why this is mangled together? How is it supposed to be working. Like a
bitarray?


>+
>+	__DPLL_PIN_MODE_MAX,
>+};
>+
>+#define DPLL_PIN_MODE_MAX (__DPLL_PIN_MODE_MAX - 1)
>+
>+/**
>+ * dpll_event - Events of dpll generic netlink family
>+ *
>+ * @DPLL_EVENT_UNSPEC - invalid event type
>+ * @DPLL_EVENT_DEVICE_CREATE - dpll device created
>+ * @DPLL_EVENT_DEVICE_DELETE - dpll device deleted
>+ * @DPLL_EVENT_DEVICE_CHANGE - attribute of dpll device or pin changed
>+ **/
>+enum dpll_event {
>+	DPLL_EVENT_UNSPEC,
>+	DPLL_EVENT_DEVICE_CREATE,
>+	DPLL_EVENT_DEVICE_DELETE,
>+	DPLL_EVENT_DEVICE_CHANGE,
>+
>+	__DPLL_EVENT_MAX,
>+};
>+
>+#define DPLL_EVENT_MAX (__DPLL_EVENT_MAX - 1)
>+
>+/**
>+ * dpll_change_type - values of events in case of device change event
>+ * (DPLL_EVENT_DEVICE_CHANGE)
>+ *
>+ * @DPLL_CHANGE_UNSPEC - invalid event type
>+ * @DPLL_CHANGE_MODE - mode changed
>+ * @DPLL_CHANGE_LOCK_STATUS - lock status changed
>+ * @DPLL_CHANGE_SOURCE_PIN - source pin changed,

Why comma at the end? Same to couple of others


>+ * @DPLL_CHANGE_TEMP - temperature changed
>+ * @DPLL_CHANGE_PIN_ADD - source pin added,
>+ * @DPLL_CHANGE_PIN_DEL - source pin deleted,
>+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
>+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
>+ * @DPLL_CHANGE_PIN_MODE - pin state changed
>+ * @DPLL_CHANGE_PIN_PRIO - pin prio changed
>+ **/
>+enum dpll_event_change {
>+	DPLL_CHANGE_UNSPEC,
>+	DPLL_CHANGE_MODE,
>+	DPLL_CHANGE_LOCK_STATUS,
>+	DPLL_CHANGE_SOURCE_PIN,
>+	DPLL_CHANGE_TEMP,
>+	DPLL_CHANGE_PIN_ADD,
>+	DPLL_CHANGE_PIN_DEL,
>+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
>+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
>+	DPLL_CHANGE_PIN_MODE,
>+	DPLL_CHANGE_PIN_PRIO,
>+
>+	__DPLL_CHANGE_MAX,
>+};
>+
>+#define DPLL_CHANGE_MAX (__DPLL_CHANGE_MAX - 1)
>+
>+/**
>+ * dpll_cmd - Commands supported by the dpll generic netlink family
>+ *
>+ * @DPLL_CMD_UNSPEC - invalid message type
>+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes of
>+ *	single dpll device and it's pins
>+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
>+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
>+ **/
>+enum dpll_cmd {
>+	DPLL_CMD_UNSPEC,
>+	DPLL_CMD_DEVICE_GET,
>+	DPLL_CMD_DEVICE_SET,
>+	DPLL_CMD_PIN_SET,

Have pin get to get list of pins, then you can have 1:1 mapping to
events and loose the enum dpll_event_change. This is the usual way to do
stuff. Events have the same cmd and message format as get.


>+
>+	__DPLL_CMD_MAX,
>+};
>+
>+#define DPLL_CMD_MAX (__DPLL_CMD_MAX - 1)
>+
>+/**
>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate how
>+ * dpll selects one of its sources to syntonize with a source.
>+ *
>+ * @DPLL_MODE_UNSPEC - invalid
>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request to dpll
>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by dpll
>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator

Why does the user care which oscilator is run internally. It's freerun,
isn't it? If you want to expose oscilator type, you should do it
elsewhere.


>+ **/
>+enum dpll_mode {
>+	DPLL_MODE_UNSPEC,
>+	DPLL_MODE_MANUAL,
>+	DPLL_MODE_AUTOMATIC,
>+	DPLL_MODE_HOLDOVER,
>+	DPLL_MODE_FREERUN,
>+	DPLL_MODE_NCO,
>+
>+	__DPLL_MODE_MAX,
>+};
>+
>+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
>+
>+/**
>+ * dpll_clock_class - enumerate quality class of a DPLL clock as specified in
>+ * Recommendation ITU-T G.8273.2/Y.1368.2.
>+ */
>+enum dpll_clock_class {
>+	DPLL_CLOCK_CLASS_UNSPEC,
>+	DPLL_CLOCK_CLASS_A,
>+	DPLL_CLOCK_CLASS_B,
>+	DPLL_CLOCK_CLASS_C,
>+
>+	__DPLL_CLOCK_CLASS_MAX,
>+};
>+
>+#define DPLL_CLOCK_CLASS_MAX (__DPLL_CLOCK_CLASS_MAX - 1)
>+
>+/**
>+ * enum dpll_type - type of dpll, integer value of enum is embedded into
>+ * name of DPLL device (DPLLA_NAME)

Yeah, I really cannot understand why you think for a second that
embedding an enum value into a name makes sense in this world :O


>+ *
>+ * @DPLL_TYPE_UNSPEC - unspecified
>+ * @DPLL_TYPE_PPS - dpll produces Pulse-Per-Second signal
>+ * @DPLL_TYPE_EEC - dpll drives the Ethernet Equipment Clock
>+ */
>+enum dpll_type {
>+	DPLL_TYPE_UNSPEC,
>+	DPLL_TYPE_PPS,
>+	DPLL_TYPE_EEC,
>+
>+	__DPLL_TYPE_MAX
>+};
>+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
>+
>+#endif /* _UAPI_LINUX_DPLL_H */
>-- 
>2.30.2
>

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-01-19 17:16     ` Jiri Pirko
  0 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-19 17:16 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk, Milena Olech,
	Michal Michalik

Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>DPLL framework is used to represent and configure DPLL devices
>in systems. Each device that has DPLL and can configure sources
>and outputs can use this framework. Netlink interface is used to
>provide configuration data and to receive notification messages
>about changes in the configuration or status of DPLL device.
>Inputs and outputs of the DPLL device are represented as special
>objects which could be dynamically added to and removed from DPLL
>device.
>
>Co-developed-by: Milena Olech <milena.olech@intel.com>
>Signed-off-by: Milena Olech <milena.olech@intel.com>
>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
>---
> MAINTAINERS                 |    8 +
> drivers/Kconfig             |    2 +
> drivers/Makefile            |    1 +
> drivers/dpll/Kconfig        |    7 +
> drivers/dpll/Makefile       |    9 +
> drivers/dpll/dpll_core.c    | 1010 +++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_core.h    |  105 ++++
> drivers/dpll/dpll_netlink.c |  883 ++++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h |   24 +
> include/linux/dpll.h        |  282 ++++++++++
> include/uapi/linux/dpll.h   |  294 ++++++++++
> 11 files changed, 2625 insertions(+)
> create mode 100644 drivers/dpll/Kconfig
> create mode 100644 drivers/dpll/Makefile
> create mode 100644 drivers/dpll/dpll_core.c
> create mode 100644 drivers/dpll/dpll_core.h
> create mode 100644 drivers/dpll/dpll_netlink.c
> create mode 100644 drivers/dpll/dpll_netlink.h
> create mode 100644 include/linux/dpll.h
> create mode 100644 include/uapi/linux/dpll.h
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index f82dd8d43c2b..de8a10b21ce8 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6411,6 +6411,14 @@ F:	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
> F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
> F:	drivers/net/ethernet/freescale/dpaa2/dpsw*
> 
>+DPLL CLOCK SUBSYSTEM
>+M:	Vadim Fedorenko <vadfed@fb.com>
>+L:	netdev@vger.kernel.org
>+S:	Maintained
>+F:	drivers/dpll/*
>+F:	include/net/dpll.h
>+F:	include/uapi/linux/dpll.h
>+
> DRBD DRIVER
> M:	Philipp Reisner <philipp.reisner@linbit.com>
> M:	Lars Ellenberg <lars.ellenberg@linbit.com>
>diff --git a/drivers/Kconfig b/drivers/Kconfig
>index 968bd0a6fd78..453df9e1210d 100644
>--- a/drivers/Kconfig
>+++ b/drivers/Kconfig
>@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
> 
> source "drivers/hte/Kconfig"
> 
>+source "drivers/dpll/Kconfig"
>+
> endmenu
>diff --git a/drivers/Makefile b/drivers/Makefile
>index bdf1c66141c9..7cbee58bc692 100644
>--- a/drivers/Makefile
>+++ b/drivers/Makefile
>@@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER)		+= counter/
> obj-$(CONFIG_MOST)		+= most/
> obj-$(CONFIG_PECI)		+= peci/
> obj-$(CONFIG_HTE)		+= hte/
>+obj-$(CONFIG_DPLL)		+= dpll/
>diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
>new file mode 100644
>index 000000000000..a4cae73f20d3
>--- /dev/null
>+++ b/drivers/dpll/Kconfig
>@@ -0,0 +1,7 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+#
>+# Generic DPLL drivers configuration
>+#
>+
>+config DPLL
>+  bool
>diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>new file mode 100644
>index 000000000000..b18cf848a010
>--- /dev/null
>+++ b/drivers/dpll/Makefile
>@@ -0,0 +1,9 @@
>+# SPDX-License-Identifier: GPL-2.0
>+#
>+# Makefile for DPLL drivers.
>+#
>+
>+obj-$(CONFIG_DPLL)          += dpll_sys.o
>+dpll_sys-y                  += dpll_core.o
>+dpll_sys-y                  += dpll_netlink.o
>+
>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..fec534f17827
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,1010 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ *  dpll_core.c - Generic DPLL Management class support.
>+ *
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>+
>+#include <linux/device.h>
>+#include <linux/err.h>
>+#include <linux/slab.h>
>+#include <linux/string.h>
>+
>+#include "dpll_core.h"
>+
>+/**
>+ * struct dpll_pin - structure for a dpll pin
>+ * @idx:		unique id number for each pin
>+ * @parent_pin:		parent pin
>+ * @type:		type of the pin
>+ * @ops:		operations this &dpll_pin supports
>+ * @priv:		pointer to private information of owner
>+ * @ref_dplls:		array of registered dplls
>+ * @description:	name to distinguish the pin
>+ */
>+struct dpll_pin {
>+	u32 idx;
>+	struct dpll_pin *parent_pin;
>+	enum dpll_pin_type type;
>+	struct dpll_pin_ops *ops;
>+	void *priv;
>+	struct xarray ref_dplls;
>+	char description[DPLL_PIN_DESC_LEN];
>+};
>+static DEFINE_MUTEX(dpll_device_xa_lock);
>+
>+static DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
>+#define DPLL_REGISTERED		XA_MARK_1
>+#define PIN_REGISTERED		XA_MARK_1

DPLL_PIN_REGISTERED


>+
>+#define ASSERT_DPLL_REGISTERED(d)                                           \
>+	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>+#define ASSERT_DPLL_NOT_REGISTERED(d)                                      \
>+	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>+
>+struct dpll_pin_ref {
>+	struct dpll_device *dpll;
>+	struct dpll_pin_ops *ops;
>+	void *priv;
>+};
>+
>+/**
>+ * dpll_device_get_by_id - find dpll device by it's id
>+ * @id: id of searched dpll
>+ *
>+ * Return: dpll_device struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_device_get_by_id(int id)
>+{
>+	struct dpll_device *dpll = NULL;
>+
>+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>+		dpll = xa_load(&dpll_device_xa, id);
>+
>+	return dpll;
>+}
>+
>+/**
>+ * dpll_device_get_by_name - find dpll device by it's id
>+ * @name: name of searched dpll
>+ *
>+ * Return: dpll_device struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_device_get_by_name(const char *name)
>+{
>+	struct dpll_device *dpll, *ret = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>+		if (!strcmp(dev_name(&dpll->dev), name)) {
>+			ret = dpll;
>+			break;
>+		}
>+	}
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return ret;
>+}
>+
>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,

Hmm, don't you want to put an owner module as an arg here as well? I
don't see how could 2 modules sanely work with the same dpll instance.


>+						enum dpll_type type, u8 idx)
>+{
>+	struct dpll_device *dpll, *ret = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>+		if (dpll->clock_id == clock_id) {
>+			if (dpll->type == type) {
>+				if (dpll->dev_driver_idx == idx) {
>+					ret = dpll;
>+					break;
>+				}
>+			}
>+		}
>+	}
>+	mutex_unlock(&dpll_device_xa_lock);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
>+
>+static void dpll_device_release(struct device *dev)
>+{
>+	struct dpll_device *dpll;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	dpll = to_dpll_device(dev);
>+	dpll_device_unregister(dpll);
>+	mutex_unlock(&dpll_device_xa_lock);
>+	dpll_device_free(dpll);
>+}
>+
>+static struct class dpll_class = {
>+	.name = "dpll",
>+	.dev_release = dpll_device_release,

Why do you want to do this? Why the driver cannot do
dpll_device_unregister/free() manually. I think it makes things easier
to read then to rely on dev garbage collector.


>+};
>+
>+struct dpll_device
>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>+		   u8 dev_driver_idx, void *priv, struct device *parent)
>+{
>+	struct dpll_device *dpll;
>+	int ret;
>+
>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>+	if (!dpll)
>+		return ERR_PTR(-ENOMEM);
>+
>+	mutex_init(&dpll->lock);
>+	dpll->ops = ops;
>+	dpll->dev.class = &dpll_class;
>+	dpll->parent = parent;
>+	dpll->type = type;
>+	dpll->dev_driver_idx = dev_driver_idx;
>+	dpll->clock_id = clock_id;
>+	dpll->clock_class = clock_class;
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>+		       xa_limit_16b, GFP_KERNEL);
>+	if (ret)
>+		goto error;
>+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
>+		     dev_driver_idx);
>+	dpll->priv = priv;
>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);

What is exactly the point of using this mark?


>+	mutex_unlock(&dpll_device_xa_lock);
>+	dpll_notify_device_create(dpll);
>+
>+	return dpll;
>+
>+error:
>+	mutex_unlock(&dpll_device_xa_lock);
>+	kfree(dpll);
>+	return ERR_PTR(ret);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_alloc);
>+
>+void dpll_device_free(struct dpll_device *dpll)
>+{
>+	WARN_ON_ONCE(!dpll);
>+	WARN_ON_ONCE(!xa_empty(&dpll->pins));
>+	xa_destroy(&dpll->pins);
>+	mutex_destroy(&dpll->lock);
>+	kfree(dpll);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_free);
>+
>+/**
>+ * dpll_device_unregister - unregister dpll device
>+ * @dpll: registered dpll pointer
>+ *
>+ * Note: It does not free the memory
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll)
>+{
>+	ASSERT_DPLL_REGISTERED(dpll);
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_erase(&dpll_device_xa, dpll->id);
>+	dpll_notify_device_delete(dpll);

Why do you need to hold the lock for notify?


>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_unregister);
>+
>+/**
>+ * dpll_id - return dpll id
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: dpll id.
>+ */
>+u32 dpll_id(struct dpll_device *dpll)
>+{
>+	return dpll->id;
>+}
>+
>+/**
>+ * dpll_pin_idx - return index of a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ *
>+ * Return: index of a pin or PIN_IDX_INVALID if not found.
>+ */
>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)
>+{
>+	struct dpll_pin *pos;
>+	unsigned long index;
>+
>+	xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED) {
>+		if (pos == pin)

What is the purpose of the lookup for the pin struct you pass as an arg?


>+			return pin->idx;
>+	}
>+
>+	return PIN_IDX_INVALID;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_idx);
>+
>+const char *dpll_dev_name(struct dpll_device *dpll)
>+{
>+	return dev_name(&dpll->dev);
>+}
>+
>+struct dpll_pin *dpll_pin_alloc(const char *description,
>+				const enum dpll_pin_type pin_type)

s/pin_type/type/


>+{
>+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
>+
>+	if (!pin)
>+		return ERR_PTR(-ENOMEM);
>+	if (pin_type <= DPLL_PIN_TYPE_UNSPEC ||
>+	    pin_type > DPLL_PIN_TYPE_MAX)
>+		return ERR_PTR(-EINVAL);

I think this check is not needed here. If driver is passing something
else, it is buggy. Idk. If you decide to leave this, put it in WARN_ON


>+
>+	strncpy(pin->description, description, DPLL_PIN_DESC_LEN);

kstrdup. Please treat the rest of the strings like that. No need to
limit the string names.


>+	pin->description[DPLL_PIN_DESC_LEN - 1] = '\0';
>+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
>+	pin->type = pin_type;
>+
>+	return pin;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_alloc);
>+
>+static int dpll_alloc_pin_on_xa(struct xarray *pins, struct dpll_pin *pin)
>+{
>+	struct dpll_pin *pos;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each(pins, index, pos) {
>+		if (pos == pin ||
>+		    !strncmp(pos->description, pin->description,
>+			     DPLL_PIN_DESC_LEN))

WARN_ON. The driver is buggy if it does something like this.


>+			return -EEXIST;
>+	}
>+
>+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
>+	if (!ret)
>+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);

What is exactly the point of having this mark?


>+
>+	return ret;
>+}
>+
>+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device *dpll,
>+			    struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin_ref *ref, *pos;
>+	unsigned long index;
>+	u32 idx;
>+
>+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
>+	if (!ref)
>+		return -ENOMEM;
>+	ref->dpll = dpll;
>+	ref->ops = ops;
>+	ref->priv = priv;
>+	if (!xa_empty(&pin->ref_dplls)) {

Pointless check. Just do iterate.


>+		xa_for_each(&pin->ref_dplls, index, pos) {
>+			if (pos->dpll == ref->dpll)
>+				return -EEXIST;
>+		}
>+	}
>+
>+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b, GFP_KERNEL);
>+}
>+
>+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device *dpll)
>+{
>+	struct dpll_pin_ref *pos;
>+	unsigned long index;
>+
>+	xa_for_each(&pin->ref_dplls, index, pos) {
>+		if (pos->dpll == dpll) {
>+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
>+			break;
>+		}
>+	}
>+}
>+
>+static int pin_deregister_from_xa(struct xarray *xa_pins, struct dpll_pin *pin)

1) dpll_ prefix
2) "deregister" is odd name
3) why don't you have it next to dpll_alloc_pin_on_xa() as it is a
   symmetric function?
4) Why exactly just xa_erase() would not do?

>+{
>+	struct dpll_pin *pos;
>+	unsigned long index;
>+
>+	xa_for_each(xa_pins, index, pos) {
>+		if (pos == pin) {
>+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));

You have an odd pattern of functions getting pin as an arg then
doing lookup for the same pin. I have to be missing to see some
black magic here :O


>+			return 0;
>+		}
>+	}
>+
>+	return -ENXIO;
>+}
>+
>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      struct dpll_pin_ops *ops, void *priv)
>+{
>+	int ret;
>+
>+	if (!pin || !ops)
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll->lock);
>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>+	if (!ret) {
>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>+		if (ret)
>+			pin_deregister_from_xa(&dpll->pins, pin);
>+	}
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>+
>+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins, int idx)
>+{
>+	struct dpll_pin *pos;
>+	unsigned long index;
>+
>+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
>+		if (pos->idx == idx)
>+			return pos;
>+	}
>+
>+	return NULL;
>+}
>+
>+/**
>+ * dpll_pin_get_by_idx - find a pin by its index
>+ * @dpll: dpll device pointer
>+ * @idx: index of pin
>+ *
>+ * Allows multiple driver instances using one physical DPLL to find
>+ * and share pin already registered with existing dpll device.
>+ *
>+ * Return: pointer if pin was found, NULL otherwise.
>+ */
>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
>+{
>+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
>+}
>+
>+	struct dpll_pin
>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char *description)
>+{
>+	struct dpll_pin *pos, *pin = NULL;
>+	unsigned long index;
>+
>+	xa_for_each(&dpll->pins, index, pos) {
>+		if (!strncmp(pos->description, description,
>+			     DPLL_PIN_DESC_LEN)) {
>+			pin = pos;
>+			break;
>+		}
>+	}
>+
>+	return pin;
>+}
>+
>+int
>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>+			 struct dpll_device *dpll,
>+			 const char *shared_pin_description,

I don't follow why you need to pass the string. You have struct dpll_pin
* in the driver. Pass that instead, avoid string to refer to kernel
object. But this is something I wrote multiple times.


>+			 struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	mutex_lock(&dpll_pin_owner->lock);
>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>+					  shared_pin_description);
>+	if (!pin) {
>+		ret = -EINVAL;
>+		goto unlock;
>+	}
>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>+unlock:
>+	mutex_unlock(&dpll_pin_owner->lock);
>+
>+	return ret;

I don't understand why there should be a separate function to register
the shared pin. As I see it, there is a pin object that could be
registered with 2 or more dpll devices. What about having:

pin = dpll_pin_alloc(desc, type, ops, priv)
dpll_pin_register(dpll_1, pin);
dpll_pin_register(dpll_2, pin);
dpll_pin_register(dpll_3, pin);

Then one pin will we in 3 xa_arrays for 3 dplls.


>+}
>+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);
>+
>+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin)

s/deregister/unregister. Be consistent in naming the functions.


>+{
>+	int ret = 0;
>+
>+	if (xa_empty(&dpll->pins))
>+		return -ENOENT;

Remove this check

>+
>+	mutex_lock(&dpll->lock);
>+	ret = pin_deregister_from_xa(&dpll->pins, pin);
>+	if (!ret)
>+		dpll_pin_ref_del(pin, dpll);
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_DEL);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_deregister);
>+
>+void dpll_pin_free(struct dpll_pin *pin)
>+{
>+	if (!xa_empty(&pin->ref_dplls))
>+		return;
>+
>+	xa_destroy(&pin->ref_dplls);
>+	kfree(pin);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_free);
>+
>+int dpll_muxed_pin_register(struct dpll_device *dpll,
>+			    const char *parent_pin_description,

Again, pass struct dpll_pin *parent, not a string.


>+			    struct dpll_pin *pin,
>+			    struct dpll_pin_ops *ops, void *priv)

Why this is a separate function? Why can't we have one function
say __dpll_pin_register()
which is called from
dpll_pin_register() - parent == null
or
dpll_muxed_pin_register() - parent == valid parent pointer
?



>+{
>+	struct dpll_pin *parent_pin;
>+	int ret;
>+
>+	if (!parent_pin_description || !pin)
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll->lock);
>+	parent_pin = dpll_pin_get_by_description(dpll, parent_pin_description);
>+	if (!parent_pin)
>+		return -EINVAL;
>+	if (parent_pin->type != DPLL_PIN_TYPE_MUX)
>+		return -EPERM;
>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>+	if (!ret)
>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>+	if (!ret)
>+		pin->parent_pin = parent_pin;
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
>+
>+/**
>+ * dpll_pin_first - get first registered pin
>+ * @dpll: registered dpll pointer
>+ * @index: found pin index (out)
>+ *
>+ * Return: dpll_pin struct if found, NULL otherwise.
>+ */
>+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long *index)
>+{
>+	*index = 0;
>+
>+	return xa_find(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>+}
>+
>+/**
>+ * dpll_pin_next - get next registered pin to the relative pin
>+ * @dpll: registered dpll pointer
>+ * @index: relative pin index (in and out)
>+ *
>+ * Return: dpll_pin struct if found, NULL otherwise.
>+ */
>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index)
>+{
>+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>+}
>+
>+/**
>+ * dpll_first - get first registered dpll device
>+ * @index: found dpll index (out)
>+ *
>+ * Return: dpll_device struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_first(unsigned long *index)
>+{
>+	*index = 0;
>+
>+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>+}
>+
>+/**
>+ * dpll_pin_next - get next registered dpll device to the relative pin
>+ * @index: relative dpll index (in and out)
>+ *
>+ * Return: dpll_pin struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_next(unsigned long *index)
>+{
>+	return xa_find_after(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>+}
>+
>+static struct dpll_pin_ref
>+*dpll_pin_find_ref(const struct dpll_device *dpll, const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long index;
>+
>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>+		if (ref->dpll != dpll)
>+			continue;
>+		else
>+			return ref;
>+	}
>+
>+	return NULL;
>+}
>+
>+/**
>+ * dpll_pin_type_get - get type of a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @type: on success - configured pin type
>+ *
>+ * Return:
>+ * * 0 - successfully got pin's type
>+ * * negative - failed to get pin's type
>+ */
>+int dpll_pin_type_get(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin,
>+		      enum dpll_pin_type *type)
>+{
>+	if (!pin)
>+		return -ENODEV;
>+	*type = pin->type;
>+
>+	return 0;
>+}
>+
>+/**
>+ * dpll_pin_signal_type_get - get signal type of a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @type: on success - configured signal type
>+ *
>+ * Return:
>+ * * 0 - successfully got signal type
>+ * * negative - failed to obtain signal type
>+ */
>+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     enum dpll_pin_signal_type *type)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->signal_type_get)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->signal_type_get(ref->dpll, pin, type);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_signal_type_set - set signal type of a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @type: type to be set
>+ *
>+ * Return:
>+ * * 0 - signal type set
>+ * * negative - failed to set signal type
>+ */
>+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     const enum dpll_pin_signal_type type)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>+		if (!ref->dpll)
>+			return -EFAULT;
>+		if (!ref || !ref->ops || !ref->ops->signal_type_set)
>+			return -EOPNOTSUPP;
>+		if (ref->dpll != dpll)
>+			mutex_lock(&ref->dpll->lock);
>+		ret = ref->ops->signal_type_set(ref->dpll, pin, type);
>+		if (ref->dpll != dpll)
>+			mutex_unlock(&ref->dpll->lock);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_signal_type_supported - check if signal type is supported on a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @type: type being checked
>+ * @supported: on success - if given signal type is supported
>+ *
>+ * Return:
>+ * * 0 - successfully got supported signal type
>+ * * negative - failed to obtain supported signal type
>+ */
>+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>+				   const struct dpll_pin *pin,
>+				   const enum dpll_pin_signal_type type,
>+				   bool *supported)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->signal_type_supported)
>+		return -EOPNOTSUPP;
>+	*supported = ref->ops->signal_type_supported(ref->dpll, pin, type);
>+
>+	return 0;
>+}
>+
>+/**
>+ * dpll_pin_mode_active - check if given mode is active on a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @mode: mode being checked
>+ * @active: on success - if mode is active
>+ *
>+ * Return:
>+ * * 0 - successfully checked if mode is active
>+ * * negative - failed to check for active mode
>+ */
>+int dpll_pin_mode_active(const struct dpll_device *dpll,
>+			  const struct dpll_pin *pin,
>+			  const enum dpll_pin_mode mode,
>+			  bool *active)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->mode_active)
>+		return -EOPNOTSUPP;
>+	*active = ref->ops->mode_active(ref->dpll, pin, mode);
>+
>+	return 0;
>+}
>+
>+/**
>+ * dpll_pin_mode_supported - check if given mode is supported on a pin
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @mode: mode being checked
>+ * @supported: on success - if mode is supported
>+ *
>+ * Return:
>+ * * 0 - successfully checked if mode is supported
>+ * * negative - failed to check for supported mode
>+ */
>+int dpll_pin_mode_supported(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     const enum dpll_pin_mode mode,
>+			     bool *supported)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->mode_supported)
>+		return -EOPNOTSUPP;
>+	*supported = ref->ops->mode_supported(ref->dpll, pin, mode);
>+
>+	return 0;
>+}
>+
>+/**
>+ * dpll_pin_mode_set - set pin's mode
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @mode: mode being set
>+ *
>+ * Return:
>+ * * 0 - successfully set the mode
>+ * * negative - failed to set the mode
>+ */
>+int dpll_pin_mode_set(const struct dpll_device *dpll,
>+		       const struct dpll_pin *pin,
>+		       const enum dpll_pin_mode mode)
>+{
>+	struct dpll_pin_ref *ref;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {

I don't understand why you need to call ops->mode_enable for all the
dplls. The pin is shared, it's the single entity. One call should be
enough? Why not? Same for other attrs, with exception of PRIO.


>+		if (!ref)
>+			return -ENODEV;
>+		if (!ref->ops || !ref->ops->mode_enable)
>+			return -EOPNOTSUPP;
>+		if (ref->dpll != dpll)
>+			mutex_lock(&ref->dpll->lock);
>+		ret = ref->ops->mode_enable(ref->dpll, pin, mode);
>+		if (ref->dpll != dpll)
>+			mutex_unlock(&ref->dpll->lock);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_custom_freq_get - get pin's custom frequency
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @freq: on success - custom frequency of a pin
>+ *
>+ * Return:
>+ * * 0 - successfully got custom frequency
>+ * * negative - failed to obtain custom frequency
>+ */
>+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin, u32 *freq)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;

How this can happen?


>+	if (!ref->ops || !ref->ops->custom_freq_get)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->custom_freq_get(ref->dpll, pin, freq);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_custom_freq_set - set pin's custom frequency
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @freq: custom frequency to be set
>+ *
>+ * Return:
>+ * * 0 - successfully set custom frequency
>+ * * negative - failed to set custom frequency
>+ */
>+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin, const u32 freq)
>+{
>+	enum dpll_pin_signal_type type;
>+	struct dpll_pin_ref *ref;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>+		if (!ref)
>+			return -ENODEV;
>+		if (!ref->ops || !ref->ops->custom_freq_set ||
>+		    !ref->ops->signal_type_get)
>+			return -EOPNOTSUPP;
>+		if (dpll != ref->dpll)
>+			mutex_lock(&ref->dpll->lock);
>+		ret = ref->ops->signal_type_get(dpll, pin, &type);
>+		if (!ret && type == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ)
>+			ret = ref->ops->custom_freq_set(ref->dpll, pin, freq);
>+		if (dpll != ref->dpll)
>+			mutex_unlock(&ref->dpll->lock);
>+		if (ret)
>+			return ret;
>+	}
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_prio_get - get pin's prio on dpll
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @prio: on success - priority of a pin on a dpll
>+ *
>+ * Return:
>+ * * 0 - successfully got priority
>+ * * negative - failed to obtain priority
>+ */
>+int dpll_pin_prio_get(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin, u32 *prio)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->prio_get)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->prio_get(ref->dpll, pin, prio);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_prio_set - set pin's prio on dpll
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @prio: priority of a pin to be set on a dpll
>+ *
>+ * Return:
>+ * * 0 - successfully set priority
>+ * * negative - failed to set the priority
>+ */
>+int dpll_pin_prio_set(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin, const u32 prio)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->prio_set)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->prio_set(ref->dpll, pin, prio);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_netifindex_get - get pin's netdev iterface index
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @netifindex: on success - index of a netdevice associated with pin
>+ *
>+ * Return:
>+ * * 0 - successfully got netdev interface index
>+ * * negative - failed to obtain netdev interface index
>+ */
>+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin,
>+			    int *netifindex)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->net_if_idx_get)
>+		return -EOPNOTSUPP;
>+	ret = ref->ops->net_if_idx_get(ref->dpll, pin, netifindex);

return right away.

>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_pin_description - provide pin's description string
>+ * @pin: registered pin pointer
>+ *
>+ * Return: pointer to a description string.
>+ */
>+const char *dpll_pin_description(struct dpll_pin *pin)
>+{
>+	return pin->description;
>+}
>+
>+/**
>+ * dpll_pin_parent - provide pin's parent pin if available
>+ * @pin: registered pin pointer
>+ *
>+ * Return: pointer to aparent if found, NULL otherwise.
>+ */
>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)

What exactly is the reason of having one line helpers to access struct
fields for a struct which is known to the caller? Unneccesary
boilerplate code. Please remove these. For pin and for dpll_device as
well.



>+{
>+	return pin->parent_pin;
>+}
>+
>+/**
>+ * dpll_mode_set - handler for dpll mode set
>+ * @dpll: registered dpll pointer
>+ * @mode: mode to be set
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode)
>+{
>+	int ret;
>+
>+	if (!dpll->ops || !dpll->ops)
>+		return -EOPNOTSUPP;
>+
>+	ret = dpll->ops->mode_set(dpll, mode);

return right away. You have this pattern on multiple places, please fix.


>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_source_idx_set - handler for selecting a dpll's source
>+ * @dpll: registered dpll pointer
>+ * @source_pin_idx: index of a source pin to e selected
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_source_idx_set(struct dpll_device *dpll, const u32 source_pin_idx)
>+{
>+	struct dpll_pin_ref *ref;
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	pin = dpll_pin_get_by_idx_from_xa(&dpll->pins, source_pin_idx);
>+	if (!pin)
>+		return -ENXIO;
>+	ref = dpll_pin_find_ref(dpll, pin);
>+	if (!ref || !ref->ops)
>+		return -EFAULT;
>+	if (!ref->ops->select)
>+		return -ENODEV;

ENODEV definitelly does not look like the correct value here.


>+	ret = ref->ops->select(ref->dpll, pin);
>+
>+	return ret;
>+}
>+
>+/**
>+ * dpll_lock - locks the dpll using internal mutex
>+ * @dpll: registered dpll pointer
>+ */
>+void dpll_lock(struct dpll_device *dpll)
>+{
>+	mutex_lock(&dpll->lock);
>+}
>+
>+/**
>+ * dpll_unlock - unlocks the dpll using internal mutex
>+ * @dpll: registered dpll pointer
>+ */
>+void dpll_unlock(struct dpll_device *dpll)
>+{
>+	mutex_unlock(&dpll->lock);
>+}
>+
>+enum dpll_pin_type dpll_pin_type(const struct dpll_pin *pin)
>+{
>+	return pin->type;
>+}
>+
>+void *dpll_priv(const struct dpll_device *dpll)
>+{
>+	return dpll->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_priv);
>+
>+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin *pin)
>+{
>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return NULL;
>+
>+	return ref->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_priv);
>+
>+static int __init dpll_init(void)
>+{
>+	int ret;
>+
>+	ret = dpll_netlink_init();
>+	if (ret)
>+		goto error;
>+
>+	ret = class_register(&dpll_class);
>+	if (ret)
>+		goto unregister_netlink;
>+
>+	return 0;
>+
>+unregister_netlink:
>+	dpll_netlink_finish();
>+error:
>+	mutex_destroy(&dpll_device_xa_lock);
>+	return ret;
>+}
>+subsys_initcall(dpll_init);
>diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
>new file mode 100644
>index 000000000000..b933d63b60c1
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.h
>@@ -0,0 +1,105 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+#ifndef __DPLL_CORE_H__
>+#define __DPLL_CORE_H__
>+
>+#include <linux/dpll.h>
>+
>+#include "dpll_netlink.h"
>+
>+#define to_dpll_device(_dev) \
>+	container_of(_dev, struct dpll_device, dev)
>+
>+/**
>+ * struct dpll_device - structure for a DPLL device
>+ * @id:		unique id number for each device
>+ * @dev:	struct device for this dpll device
>+ * @parent:	parent device
>+ * @ops:	operations this &dpll_device supports
>+ * @lock:	mutex to serialize operations
>+ * @type:	type of a dpll
>+ * @priv:	pointer to private information of owner
>+ * @pins:	list of pointers to pins registered with this dpll
>+ * @clock_id:	unique identifier (clock_id) of a dpll
>+ * @clock_class	quality class of a DPLL clock
>+ * @dev_driver_idx: provided by driver for
>+ */
>+struct dpll_device {
>+	u32 id;
>+	struct device dev;
>+	struct device *parent;
>+	struct dpll_device_ops *ops;
>+	struct mutex lock;
>+	enum dpll_type type;
>+	void *priv;
>+	struct xarray pins;
>+	u64 clock_id;
>+	enum dpll_clock_class clock_class;
>+	u8 dev_driver_idx;
>+};
>+
>+#define for_each_pin_on_dpll(dpll, pin, i)			\
>+	for (pin = dpll_pin_first(dpll, &i); pin != NULL;	\
>+	     pin = dpll_pin_next(dpll, &i))

What is the purpose for this macro and dpll_pin_first/next() helper?
Why is this not equivalent to:
xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED)


>+
>+#define for_each_dpll(dpll, i)                         \
>+	for (dpll = dpll_first(&i); dpll != NULL; dpll = dpll_next(&i))

Same here, why this macro and helpers are needed?


>+
>+struct dpll_device *dpll_device_get_by_id(int id);
>+
>+struct dpll_device *dpll_device_get_by_name(const char *name);
>+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long *index);
>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index);
>+struct dpll_device *dpll_first(unsigned long *index);
>+struct dpll_device *dpll_next(unsigned long *index);
>+void dpll_device_unregister(struct dpll_device *dpll);
>+u32 dpll_id(struct dpll_device *dpll);
>+const char *dpll_dev_name(struct dpll_device *dpll);
>+void dpll_lock(struct dpll_device *dpll);
>+void dpll_unlock(struct dpll_device *dpll);
>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>+int dpll_pin_type_get(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin,
>+		      enum dpll_pin_type *type);
>+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     enum dpll_pin_signal_type *type);
>+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     const enum dpll_pin_signal_type type);
>+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>+				   const struct dpll_pin *pin,
>+				   const enum dpll_pin_signal_type type,
>+				   bool *supported);
>+int dpll_pin_mode_active(const struct dpll_device *dpll,
>+			 const struct dpll_pin *pin,
>+			 const enum dpll_pin_mode mode,
>+			 bool *active);
>+int dpll_pin_mode_supported(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin,
>+			    const enum dpll_pin_mode mode,
>+			    bool *supported);
>+int dpll_pin_mode_set(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin,
>+		      const enum dpll_pin_mode mode);
>+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin, u32 *freq);
>+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin, const u32 freq);
>+int dpll_pin_prio_get(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin, u32 *prio);
>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx);
>+int dpll_pin_prio_set(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin, const u32 prio);
>+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin,
>+			    int *netifindex);
>+const char *dpll_pin_description(struct dpll_pin *pin);
>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin);
>+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode);
>+int dpll_source_idx_set(struct dpll_device *dpll, const u32 source_pin_idx);
>+
>+#endif
>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>new file mode 100644
>index 000000000000..91a1e5025ab2
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -0,0 +1,883 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Generic netlink for DPLL management framework
>+ *
>+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ *
>+ */
>+#include <linux/module.h>
>+#include <linux/kernel.h>
>+#include <net/genetlink.h>
>+#include "dpll_core.h"
>+
>+#include <uapi/linux/dpll.h>
>+
>+static const struct genl_multicast_group dpll_mcgrps[] = {
>+	{ .name = DPLL_MONITOR_GROUP_NAME,  },
>+};
>+
>+static const struct nla_policy dpll_cmd_device_get_policy[] = {
>+	[DPLLA_ID]		= { .type = NLA_U32 },
>+	[DPLLA_NAME]		= { .type = NLA_STRING,
>+				    .len = DPLL_NAME_LEN },
>+	[DPLLA_FILTER]		= { .type = NLA_U32 },
>+};
>+
>+static const struct nla_policy dpll_cmd_device_set_policy[] = {
>+	[DPLLA_ID]		= { .type = NLA_U32 },
>+	[DPLLA_NAME]		= { .type = NLA_STRING,
>+				    .len = DPLL_NAME_LEN },
>+	[DPLLA_MODE]		= { .type = NLA_U32 },
>+	[DPLLA_SOURCE_PIN_IDX]	= { .type = NLA_U32 },
>+};
>+
>+static const struct nla_policy dpll_cmd_pin_set_policy[] = {
>+	[DPLLA_ID]		= { .type = NLA_U32 },
>+	[DPLLA_PIN_IDX]		= { .type = NLA_U32 },
>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>+	[DPLLA_PIN_MODE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>+};
>+
>+struct dpll_dump_ctx {
>+	int dump_filter;
>+};
>+
>+static struct genl_family dpll_gnl_family;
>+
>+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
>+{
>+	return (struct dpll_dump_ctx *)cb->ctx;
>+}
>+
>+static int dpll_msg_add_id(struct sk_buff *msg, u32 id)
>+{
>+	if (nla_put_u32(msg, DPLLA_ID, id))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_name(struct sk_buff *msg, const char *name)
>+{
>+	if (nla_put_string(msg, DPLLA_NAME, name))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int __dpll_msg_add_mode(struct sk_buff *msg, enum dplla msg_type,
>+			       enum dpll_mode mode)
>+{
>+	if (nla_put_s32(msg, msg_type, mode))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll)
>+{
>+	enum dpll_mode m;
>+	int ret;
>+
>+	if (!dpll->ops->mode_get)
>+		return 0;
>+	ret = dpll->ops->mode_get(dpll, &m);
>+	if (ret)
>+		return ret;
>+
>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>+}
>+
>+static int
>+dpll_msg_add_modes_supported(struct sk_buff *msg,
>+			     const struct dpll_device *dpll)
>+{
>+	enum dpll_mode i;
>+	int ret = 0;
>+
>+	if (!dpll->ops->mode_supported)
>+		return ret;
>+
>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>+		if (dpll->ops->mode_supported(dpll, i)) {
>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_msg_add_clock_id(struct sk_buff *msg,
>+				 const struct dpll_device *dpll)
>+{
>+	if (nla_put_64bit(msg, DPLLA_CLOCK_ID, sizeof(dpll->clock_id),
>+			  &dpll->clock_id, 0))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_clock_class(struct sk_buff *msg,
>+				    const struct dpll_device *dpll)
>+{
>+	if (nla_put_s32(msg, DPLLA_CLOCK_CLASS, dpll->clock_class))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	u32 source_idx;
>+	int ret;
>+
>+	if (!dpll->ops->source_pin_idx_get)
>+		return 0;
>+	ret = dpll->ops->source_pin_idx_get(dpll, &source_idx);
>+	if (ret)
>+		return ret;
>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	enum dpll_lock_status s;
>+	int ret;
>+
>+	if (!dpll->ops->lock_status_get)
>+		return 0;
>+	ret = dpll->ops->lock_status_get(dpll, &s);
>+	if (ret)
>+		return ret;
>+	if (nla_put_s32(msg, DPLLA_LOCK_STATUS, s))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	s32 temp;
>+	int ret;
>+
>+	if (!dpll->ops->temp_get)
>+		return 0;
>+	ret = dpll->ops->temp_get(dpll, &temp);
>+	if (ret)
>+		return ret;
>+	if (nla_put_u32(msg, DPLLA_TEMP, temp))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_idx(struct sk_buff *msg, u32 pin_idx)
>+{
>+	if (nla_put_u32(msg, DPLLA_PIN_IDX, pin_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_description(struct sk_buff *msg,
>+					const char *description)
>+{
>+	if (nla_put_string(msg, DPLLA_PIN_DESCRIPTION, description))

I don't understand the reason to have these helpers. I said that before,
just call nla_put_* directly and avoid these unnecessary boilerplate
unctions.


>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_parent_idx(struct sk_buff *msg, u32 parent_idx)
>+{
>+	if (nla_put_u32(msg, DPLLA_PIN_PARENT_IDX, parent_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin)
>+{
>+	enum dpll_pin_type t;
>+
>+	if (dpll_pin_type_get(dpll, pin, &t))
>+		return 0;
>+
>+	if (nla_put_s32(msg, DPLLA_PIN_TYPE, t))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int __dpll_msg_add_pin_signal_type(struct sk_buff *msg,
>+					  enum dplla attr,
>+					  enum dpll_pin_signal_type type)
>+{
>+	if (nla_put_s32(msg, attr, type))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_signal_type(struct sk_buff *msg,
>+					const struct dpll_device *dpll,
>+					const struct dpll_pin *pin)
>+{
>+	enum dpll_pin_signal_type t;

s/t/type/


>+	int ret;
>+
>+	if (dpll_pin_signal_type_get(dpll, pin, &t))

Why don't you propagate the error value?


>+		return 0;
>+	ret = __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>+	if (ret)
>+		return ret;
>+
>+	if (t == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) {
>+		u32 freq;
>+
>+		if (dpll_pin_custom_freq_get(dpll, pin, &freq))
>+			return 0;
>+		if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>+			return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>+					const struct dpll_device *dpll,
>+					const struct dpll_pin *pin)
>+{
>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>+	enum dpll_pin_signal_type i;
>+	bool supported;
>+
>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>+		if (dpll_pin_signal_type_supported(dpll, pin, i, &supported))
>+			continue;
>+		if (supported) {
>+			int ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>+
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>+				   const struct dpll_device *dpll,
>+				   const struct dpll_pin *pin)
>+{
>+	enum dpll_pin_mode i;
>+	bool active;
>+
>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>+			return 0;
>+		if (active)
>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))

Why this is signed?


>+				return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_modes_supported(struct sk_buff *msg,
>+					     const struct dpll_device *dpll,
>+					     const struct dpll_pin *pin)
>+{
>+	enum dpll_pin_mode i;
>+	bool supported;
>+
>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>+		if (dpll_pin_mode_supported(dpll, pin, i, &supported))
>+			return 0;
>+		if (supported)
>+			if (nla_put_s32(msg, DPLLA_PIN_MODE_SUPPORTED, i))

Here too. Please check the rest, you should not need to put signed
values.


>+				return -EMSGSIZE;
>+	}
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin)
>+{
>+	u32 prio;
>+
>+	if (dpll_pin_prio_get(dpll, pin, &prio))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_PIN_PRIO, prio))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin)
>+{
>+	int netifindex;
>+
>+	if (dpll_pin_netifindex_get(dpll, pin, &netifindex))
>+		return 0;
>+	if (nla_put_s32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+__dpll_cmd_device_dump_one(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
>+
>+	return ret;
>+}
>+
>+static int
>+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_device *dpll,
>+			struct dpll_pin *pin)
>+{
>+	struct dpll_pin *parent = NULL;
>+	int ret;
>+
>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_description(msg, dpll_pin_description(pin));
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_type(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	parent = dpll_pin_parent(pin);
>+	if (parent) {
>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>+								    parent));
>+		if (ret)
>+			return ret;
>+	}
>+	ret = dpll_msg_add_pin_signal_type(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_signal_types_supported(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_modes(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_modes_supported(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_prio(msg, dpll, pin);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_pin_netifindex(msg, dpll, pin);
>+
>+	return ret;
>+}
>+
>+static int __dpll_cmd_dump_pins(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	struct dpll_pin *pin;
>+	struct nlattr *attr;
>+	unsigned long i;
>+	int ret = 0;
>+
>+	for_each_pin_on_dpll(dpll, pin, i) {
>+		attr = nla_nest_start(msg, DPLLA_PIN);
>+		if (!attr) {
>+			ret = -EMSGSIZE;
>+			goto nest_cancel;
>+		}
>+		ret = __dpll_cmd_pin_dump_one(msg, dpll, pin);
>+		if (ret)
>+			goto nest_cancel;
>+		nla_nest_end(msg, attr);
>+	}
>+
>+	return ret;
>+
>+nest_cancel:
>+	nla_nest_cancel(msg, attr);
>+	return ret;
>+}
>+
>+static int
>+__dpll_cmd_dump_status(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	int ret = dpll_msg_add_source_pin(msg, dpll);
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_temp(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_lock_status(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_mode(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_modes_supported(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_clock_id(msg, dpll);
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_clock_class(msg, dpll);
>+
>+	return ret;
>+}
>+
>+static int
>+dpll_device_dump_one(struct dpll_device *dpll, struct sk_buff *msg,
>+		     int dump_filter)
>+{
>+	int ret;
>+
>+	dpll_lock(dpll);
>+	ret = __dpll_cmd_device_dump_one(msg, dpll);
>+	if (ret)
>+		goto out_unlock;
>+
>+	if (dump_filter & DPLL_FILTER_STATUS) {
>+		ret = __dpll_cmd_dump_status(msg, dpll);
>+		if (ret) {
>+			if (ret != -EMSGSIZE)
>+				ret = -EAGAIN;
>+			goto out_unlock;
>+		}
>+	}
>+	if (dump_filter & DPLL_FILTER_PINS)
>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>+	dpll_unlock(dpll);
>+
>+	return ret;
>+out_unlock:
>+	dpll_unlock(dpll);
>+	return ret;
>+}
>+
>+static int
>+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
>+			 struct dpll_pin *pin, struct genl_info *info)
>+{
>+	enum dpll_pin_signal_type st;
>+	enum dpll_pin_mode mode;
>+	struct nlattr *a;
>+	int rem, ret = 0;
>+	u32 prio, freq;
>+
>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>+			  genlmsg_len(info->genlhdr), rem) {
>+		switch (nla_type(a)) {
>+		case DPLLA_PIN_SIGNAL_TYPE:
>+			st = nla_get_s32(a);
>+			ret = dpll_pin_signal_type_set(dpll, pin, st);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_CUSTOM_FREQ:
>+			freq = nla_get_u32(a);
>+			ret = dpll_pin_custom_freq_set(dpll, pin, freq);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_MODE:
>+			mode = nla_get_s32(a);
>+			ret = dpll_pin_mode_set(dpll, pin, mode);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_PRIO:
>+			prio = nla_get_u32(a);
>+			ret = dpll_pin_prio_set(dpll, pin, prio);
>+			if (ret)
>+				return ret;
>+			break;
>+		default:
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_cmd_pin_set(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	struct nlattr **attrs = info->attrs;
>+	struct dpll_pin *pin;
>+	int pin_id;
>+
>+	if (!attrs[DPLLA_PIN_IDX])
>+		return -EINVAL;
>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>+	dpll_lock(dpll);
>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>+	dpll_unlock(dpll);
>+	if (!pin)
>+		return -ENODEV;
>+	return dpll_pin_set_from_nlattr(dpll, pin, info);
>+}
>+
>+enum dpll_mode dpll_msg_read_mode(struct nlattr *a)
>+{
>+	return nla_get_s32(a);
>+}
>+
>+u32 dpll_msg_read_source_pin_id(struct nlattr *a)
>+{
>+	return nla_get_u32(a);
>+}
>+
>+static int
>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>+{
>+	enum dpll_mode m;
>+	struct nlattr *a;
>+	int rem, ret = 0;
>+	u32 source_pin;
>+
>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>+			  genlmsg_len(info->genlhdr), rem) {
>+		switch (nla_type(a)) {
>+		case DPLLA_MODE:
>+			m = dpll_msg_read_mode(a);
>+
>+			ret = dpll_mode_set(dpll, m);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_SOURCE_PIN_IDX:
>+			source_pin = dpll_msg_read_source_pin_id(a);
>+
>+			ret = dpll_source_idx_set(dpll, source_pin);
>+			if (ret)
>+				return ret;
>+			break;
>+		default:
>+			break;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_cmd_device_set(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_device *dpll = info->user_ptr[0];
>+
>+	return dpll_set_from_nlattr(dpll, info);
>+}
>+
>+static int
>+dpll_cmd_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>+	struct dpll_device *dpll;
>+	struct nlattr *hdr;
>+	unsigned long i;
>+	int ret;
>+
>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
>+			  &dpll_gnl_family, 0, DPLL_CMD_DEVICE_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	for_each_dpll(dpll, i) {
>+		ret = dpll_device_dump_one(dpll, skb, ctx->dump_filter);
>+		if (ret)
>+			break;
>+	}
>+
>+	if (ret)
>+		genlmsg_cancel(skb, hdr);
>+	else
>+		genlmsg_end(skb, hdr);
>+
>+	return ret;
>+}
>+
>+static int dpll_cmd_device_get(struct sk_buff *skb, struct genl_info *info)
>+{
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	struct nlattr **attrs = info->attrs;
>+	struct sk_buff *msg;
>+	int dump_filter = 0;
>+	struct nlattr *hdr;
>+	int ret;
>+
>+	if (attrs[DPLLA_FILTER])
>+		dump_filter = nla_get_s32(attrs[DPLLA_FILTER]);
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+	hdr = genlmsg_put_reply(msg, info, &dpll_gnl_family, 0,
>+				DPLL_CMD_DEVICE_GET);
>+	if (!hdr)
>+		return -EMSGSIZE;
>+
>+	ret = dpll_device_dump_one(dpll, msg, dump_filter);
>+	if (ret)
>+		goto out_free_msg;
>+	genlmsg_end(msg, hdr);
>+
>+	return genlmsg_reply(msg, info);
>+
>+out_free_msg:
>+	nlmsg_free(msg);
>+	return ret;
>+
>+}
>+
>+static int dpll_cmd_device_get_start(struct netlink_callback *cb)
>+{
>+	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
>+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>+	struct nlattr *attr = info->attrs[DPLLA_FILTER];
>+
>+	if (attr)
>+		ctx->dump_filter = nla_get_s32(attr);
>+	else
>+		ctx->dump_filter = 0;
>+
>+	return 0;
>+}
>+
>+static int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+			 struct genl_info *info)
>+{
>+	struct dpll_device *dpll_id = NULL, *dpll_name = NULL;
>+
>+	if (!info->attrs[DPLLA_ID] &&
>+	    !info->attrs[DPLLA_NAME])
>+		return -EINVAL;
>+
>+	if (info->attrs[DPLLA_ID]) {
>+		u32 id = nla_get_u32(info->attrs[DPLLA_ID]);
>+
>+		dpll_id = dpll_device_get_by_id(id);
>+		if (!dpll_id)
>+			return -ENODEV;
>+		info->user_ptr[0] = dpll_id;
>+	}
>+	if (info->attrs[DPLLA_NAME]) {
>+		const char *name = nla_data(info->attrs[DPLLA_NAME]);
>+
>+		dpll_name = dpll_device_get_by_name(name);
>+		if (!dpll_name)
>+			return -ENODEV;
>+
>+		if (dpll_id && dpll_name != dpll_id)
>+			return -EINVAL;
>+		info->user_ptr[0] = dpll_name;
>+	}
>+
>+	return 0;
>+}
>+
>+static const struct genl_ops dpll_ops[] = {
>+	{
>+		.cmd	= DPLL_CMD_DEVICE_GET,
>+		.flags  = GENL_UNS_ADMIN_PERM,
>+		.start	= dpll_cmd_device_get_start,
>+		.dumpit	= dpll_cmd_device_dump,
>+		.doit	= dpll_cmd_device_get,
>+		.policy	= dpll_cmd_device_get_policy,
>+		.maxattr = ARRAY_SIZE(dpll_cmd_device_get_policy) - 1,
>+	},
>+	{
>+		.cmd	= DPLL_CMD_DEVICE_SET,
>+		.flags	= GENL_UNS_ADMIN_PERM,
>+		.doit	= dpll_cmd_device_set,
>+		.policy	= dpll_cmd_device_set_policy,
>+		.maxattr = ARRAY_SIZE(dpll_cmd_device_set_policy) - 1,
>+	},
>+	{
>+		.cmd	= DPLL_CMD_PIN_SET,
>+		.flags	= GENL_UNS_ADMIN_PERM,
>+		.doit	= dpll_cmd_pin_set,
>+		.policy	= dpll_cmd_pin_set_policy,
>+		.maxattr = ARRAY_SIZE(dpll_cmd_pin_set_policy) - 1,
>+	},
>+};
>+
>+static struct genl_family dpll_family __ro_after_init = {
>+	.hdrsize	= 0,

No need for static.


>+	.name		= DPLL_FAMILY_NAME,
>+	.version	= DPLL_VERSION,
>+	.ops		= dpll_ops,
>+	.n_ops		= ARRAY_SIZE(dpll_ops),
>+	.mcgrps		= dpll_mcgrps,
>+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
>+	.pre_doit	= dpll_pre_doit,
>+	.parallel_ops   = true,
>+};
>+
>+static int dpll_event_device_id(struct sk_buff *msg, struct dpll_device *dpll)
>+{
>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
>+
>+	return ret;
>+}
>+
>+static int dpll_event_device_change(struct sk_buff *msg,
>+				    struct dpll_device *dpll,
>+				    struct dpll_pin *pin,
>+				    enum dpll_event_change event)
>+{
>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = nla_put_s32(msg, DPLLA_CHANGE_TYPE, event);
>+	if (ret)
>+		return ret;
>+	switch (event)	{
>+	case DPLL_CHANGE_PIN_ADD:
>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>+	case DPLL_CHANGE_PIN_MODE:
>+	case DPLL_CHANGE_PIN_PRIO:
>+		ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>+		break;
>+	default:
>+		break;
>+	}
>+
>+	return ret;
>+}
>+
>+/*
>+ * Generic netlink DPLL event encoding
>+ */
>+static int dpll_send_event_create(enum dpll_event event,
>+				  struct dpll_device *dpll)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+
>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>+	if (!hdr)
>+		goto out_free_msg;
>+
>+	ret = dpll_event_device_id(msg, dpll);
>+	if (ret)
>+		goto out_cancel_msg;
>+	genlmsg_end(msg, hdr);
>+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
>+
>+	return 0;
>+
>+out_cancel_msg:
>+	genlmsg_cancel(msg, hdr);
>+out_free_msg:
>+	nlmsg_free(msg);
>+
>+	return ret;
>+}
>+
>+/*
>+ * Generic netlink DPLL event encoding
>+ */
>+static int dpll_send_event_change(struct dpll_device *dpll,
>+				  struct dpll_pin *pin,
>+				  enum dpll_event_change event)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+
>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, DPLL_EVENT_DEVICE_CHANGE);
>+	if (!hdr)
>+		goto out_free_msg;
>+
>+	ret = dpll_event_device_change(msg, dpll, pin, event);
>+	if (ret)
>+		goto out_cancel_msg;
>+	genlmsg_end(msg, hdr);
>+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
>+
>+	return 0;
>+
>+out_cancel_msg:
>+	genlmsg_cancel(msg, hdr);
>+out_free_msg:
>+	nlmsg_free(msg);
>+
>+	return ret;
>+}
>+
>+int dpll_notify_device_create(struct dpll_device *dpll)
>+{
>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
>+}
>+
>+int dpll_notify_device_delete(struct dpll_device *dpll)
>+{
>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
>+}
>+
>+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change event)
>+{
>+	return dpll_send_event_change(dpll, NULL, event);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_notify);
>+
>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dpll_event_change event)
>+{
>+	return dpll_send_event_change(dpll, pin, event);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_notify);
>+
>+int __init dpll_netlink_init(void)
>+{
>+	return genl_register_family(&dpll_family);
>+}
>+
>+void dpll_netlink_finish(void)
>+{
>+	genl_unregister_family(&dpll_family);
>+}
>+
>+void __exit dpll_netlink_fini(void)
>+{
>+	dpll_netlink_finish();
>+}
>diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
>new file mode 100644
>index 000000000000..8e50b2493027
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.h
>@@ -0,0 +1,24 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+/**
>+ * dpll_notify_device_create - notify that the device has been created
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_notify_device_create(struct dpll_device *dpll);
>+
>+
>+/**
>+ * dpll_notify_device_delete - notify that the device has been deleted
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_notify_device_delete(struct dpll_device *dpll);
>+
>+int __init dpll_netlink_init(void);
>+void dpll_netlink_finish(void);
>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>new file mode 100644
>index 000000000000..fcba46ea1b7b
>--- /dev/null
>+++ b/include/linux/dpll.h
>@@ -0,0 +1,282 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>+ */
>+
>+#ifndef __DPLL_H__
>+#define __DPLL_H__
>+
>+#include <uapi/linux/dpll.h>
>+#include <linux/device.h>
>+
>+struct dpll_device;
>+struct dpll_pin;
>+
>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>+
>+struct dpll_device_ops {
>+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode *mode);
>+	int (*mode_set)(const struct dpll_device *dpll,
>+			const enum dpll_mode mode);
>+	bool (*mode_supported)(const struct dpll_device *dpll,
>+			       const enum dpll_mode mode);
>+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
>+				  u32 *pin_idx);
>+	int (*lock_status_get)(const struct dpll_device *dpll,
>+			       enum dpll_lock_status *status);
>+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp);
>+};
>+
>+struct dpll_pin_ops {
>+	int (*signal_type_get)(const struct dpll_device *dpll,
>+			       const struct dpll_pin *pin,
>+			       enum dpll_pin_signal_type *type);
>+	int (*signal_type_set)(const struct dpll_device *dpll,
>+			       const struct dpll_pin *pin,
>+			       const enum dpll_pin_signal_type type);
>+	bool (*signal_type_supported)(const struct dpll_device *dpll,
>+				      const struct dpll_pin *pin,
>+				      const enum dpll_pin_signal_type type);
>+	int (*custom_freq_set)(const struct dpll_device *dpll,
>+			       const struct dpll_pin *pin,
>+			       const u32 custom_freq);
>+	int (*custom_freq_get)(const struct dpll_device *dpll,
>+			       const struct dpll_pin *pin,
>+			       u32 *custom_freq);
>+	bool (*mode_active)(const struct dpll_device *dpll,
>+			     const struct dpll_pin *pin,
>+			     const enum dpll_pin_mode mode);
>+	int (*mode_enable)(const struct dpll_device *dpll,
>+			    const struct dpll_pin *pin,
>+			    const enum dpll_pin_mode mode);
>+	bool (*mode_supported)(const struct dpll_device *dpll,
>+				const struct dpll_pin *pin,
>+				const enum dpll_pin_mode mode);
>+	int (*prio_get)(const struct dpll_device *dpll,
>+			const struct dpll_pin *pin,
>+			u32 *prio);
>+	int (*prio_set)(const struct dpll_device *dpll,
>+			const struct dpll_pin *pin,
>+			const u32 prio);
>+	int (*net_if_idx_get)(const struct dpll_device *dpll,
>+			      const struct dpll_pin *pin,
>+			      int *net_if_idx);
>+	int (*select)(const struct dpll_device *dpll,
>+		      const struct dpll_pin *pin);

Could you please pass extack to all of the ops? I think it is important
to give the user the meaningfull error message from the start.


>+};
>+
>+/**
>+ * dpll_device_alloc - allocate memory for a new dpll_device object
>+ * @ops: pointer to dpll operations structure
>+ * @type: type of a dpll being allocated
>+ * @clock_id: a system unique number for a device
>+ * @clock_class: quality class of a DPLL clock
>+ * @dev_driver_idx: index of dpll device on parent device
>+ * @priv: private data of a registerer
>+ * @parent: device structure of a module registering dpll device
>+ *
>+ * Allocate memory for a new dpll and initialize it with its type, name,
>+ * callbacks and private data pointer.
>+ *
>+ * Name is generated based on: parent driver, type and dev_driver_idx.
>+ * Finding allocated and registered dpll device is also possible with
>+ * the: clock_id, type and dev_driver_idx. This way dpll device can be
>+ * shared by multiple instances of a device driver.
>+ *
>+ * Returns:
>+ * * pointer to initialized dpll - success
>+ * * NULL - memory allocation fail
>+ */
>+struct dpll_device
>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>+		   u8 dev_driver_idx, void *priv, struct device *parent);
>+
>+/**
>+ * dpll_device_unregister - unregister registered dpll
>+ * @dpll: pointer to dpll
>+ *
>+ * Unregister the dpll from the subsystem, make it unavailable for netlink
>+ * API users.
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_device_free - free dpll memory
>+ * @dpll: pointer to dpll
>+ *
>+ * Free memory allocated with ``dpll_device_alloc(..)``
>+ */
>+void dpll_device_free(struct dpll_device *dpll);


Could you please sort the functions? I mean, dpll_device_unregister() in
currently in the middle of dpll_device_alloc() and dpll_device_free()

Also, there is no dpll_device_register(), that is odd.


>+
>+/**
>+ * dpll_priv - get private data
>+ * @dpll: pointer to dpll
>+ *
>+ * Obtain private data pointer passed to dpll subsystem when allocating
>+ * device with ``dpll_device_alloc(..)``
>+ */
>+void *dpll_priv(const struct dpll_device *dpll);
>+
>+/**
>+ * dpll_pin_priv - get private data
>+ * @dpll: pointer to dpll
>+ *
>+ * Obtain private pin data pointer passed to dpll subsystem when pin
>+ * was registered with dpll.
>+ */
>+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_idx - get pin idx
>+ * @dpll: pointer to dpll
>+ * @pin: pointer to a pin
>+ *
>+ * Obtain pin index of given pin on given dpll.
>+ *
>+ * Return: PIN_IDX_INVALID - if failed to find pin, otherwise pin index
>+ */
>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);

You don't use this in driver (and I don't see any need for that). Remove
from the public header.


>+
>+/**
>+ * dpll_shared_pin_register - share a pin between dpll devices
>+ * @dpll_pin_owner: a dpll already registered with a pin
>+ * @dpll: a dpll being registered with a pin
>+ * @shared_pin_description: identifies pin registered with dpll device
>+ *	(@dpll_pin_owner) which is now being registered with new dpll (@dpll)
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ *
>+ * Register a pin already registered with different dpll device.
>+ * Allow to share a single pin within multiple dpll instances.
>+ *
>+ * Returns:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+int
>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>+			 struct dpll_device *dpll,
>+			 const char *shared_pin_description,
>+			 struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_pin_alloc - allocate memory for a new dpll_pin object
>+ * @description: pointer to string description of a pin with max length
>+ * equal to PIN_DESC_LEN
>+ * @type: type of allocated pin
>+ *
>+ * Allocate memory for a new pin and initialize its resources.
>+ *
>+ * Returns:
>+ * * pointer to initialized pin - success
>+ * * NULL - memory allocation fail
>+ */
>+struct dpll_pin *dpll_pin_alloc(const char *description,
>+				const enum dpll_pin_type type);
>+
>+/**
>+ * dpll_pin_register - register pin with a dpll device
>+ * @dpll: pointer to dpll object to register pin with
>+ * @pin: pointer to allocated pin object being registered with dpll
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops
>+ *
>+ * Register previously allocated pin object with a dpll device.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this dpll,
>+ * * -EBUSY - couldn't allocate id for a pin.
>+ */
>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_pin_deregister - deregister pin from a dpll device
>+ * @dpll: pointer to dpll object to deregister pin from
>+ * @pin: pointer to allocated pin object being deregistered from dpll
>+ *
>+ * Deregister previously registered pin object from a dpll device.
>+ *
>+ * Return:
>+ * * 0 - pin was successfully deregistered from this dpll device,
>+ * * -ENXIO - given pin was not registered with this dpll device,
>+ * * -EINVAL - pin pointer is not valid.
>+ */
>+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_free - free memory allocated for a pin
>+ * @pin: pointer to allocated pin object being freed
>+ *
>+ * Shared pins must be deregistered from all dpll devices before freeing them,
>+ * otherwise the memory won't be freed.
>+ */
>+void dpll_pin_free(struct dpll_pin *pin);
>+
>+/**
>+ * dpll_muxed_pin_register - register a pin to a muxed-type pin
>+ * @parent_pin_description: parent pin description as given on it's allocation
>+ * @pin: pointer to allocated pin object being registered with a parent pin
>+ * @ops: struct with pin ops callbacks
>+ * @priv: private data pointer passed when calling callback ops*
>+ *
>+ * In case of multiplexed pins, allows registring them under a single
>+ * parent pin.
>+ *
>+ * Return:
>+ * * 0 - if pin was registered with a parent pin,
>+ * * -ENOMEM - failed to allocate memory,
>+ * * -EEXIST - pin already registered with this parent pin,
>+ * * -EBUSY - couldn't assign id for a pin.
>+ */
>+int dpll_muxed_pin_register(struct dpll_device *dpll,
>+			    const char *parent_pin_description,
>+			    struct dpll_pin *pin,
>+			    struct dpll_pin_ops *ops, void *priv);
>+
>+/**
>+ * dpll_device_get_by_clock_id - find a dpll by its clock_id, type and index
>+ * @clock_id: clock_id of dpll, as given by driver on ``dpll_device_alloc``
>+ * @type: type of dpll, as given by driver on ``dpll_device_alloc``
>+ * @idx: index of dpll, as given by driver on ``dpll_device_alloc``
>+ *
>+ * Allows multiple driver instances using one physical DPLL to find
>+ * and share already registered DPLL device.
>+ *
>+ * Return: pointer if device was found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>+						enum dpll_type type, u8 idx);
>+
>+/**
>+ * dpll_device_notify - notify on dpll device change
>+ * @dpll: dpll device pointer
>+ * @event: type of change
>+ *
>+ * Broadcast event to the netlink multicast registered listeners.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change event);
>+
>+/**
>+ * dpll_pin_notify - notify on dpll pin change
>+ * @dpll: dpll device pointer
>+ * @pin: dpll pin pointer
>+ * @event: type of change
>+ *
>+ * Broadcast event to the netlink multicast registered listeners.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - error
>+ */
>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>+		    enum dpll_event_change event);

You don't use this from driver, remove it from the public header.


>+
>+#endif
>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>new file mode 100644
>index 000000000000..b7dbdd814b5c
>--- /dev/null
>+++ b/include/uapi/linux/dpll.h
>@@ -0,0 +1,294 @@
>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>+#ifndef _UAPI_LINUX_DPLL_H
>+#define _UAPI_LINUX_DPLL_H
>+
>+#define DPLL_NAME_LEN		32
>+#define DPLL_DESC_LEN		20
>+#define DPLL_PIN_DESC_LEN	20

I don't see why to limit this. Those strings are read only. See
DEVLINK_ATTR_BUS_NAME for example.


>+
>+/* Adding event notification support elements */
>+#define DPLL_FAMILY_NAME	"dpll"
>+#define DPLL_VERSION		0x01
>+#define DPLL_MONITOR_GROUP_NAME	"monitor"
>+
>+#define DPLL_FILTER_PINS	1
>+#define DPLL_FILTER_STATUS	2

Why again do we need any filtering here?


>+
>+/* dplla - Attributes of dpll generic netlink family
>+ *
>+ * @DPLLA_UNSPEC - invalid attribute
>+ * @DPLLA_ID - ID of a dpll device (unsigned int)
>+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH size)
>+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
>+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum dpll_mode)
>+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll

IDX


>+ *	(unsigned int)
>+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
>+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)

Hmm, wouldn't it be better to have it as 1/10 of Celsius degree for
example?


>+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
>+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum dpll_clock_class)
>+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests (int,
>+ *	sum of DPLL_DUMP_FILTER_* defines)
>+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
>+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
>+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by driver
>+ *	(char array of PIN_DESC_LEN size)
>+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
>+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
>+ *	(enum dpll_pin_signal_type)
>+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
>+ *	(enum dpll_pin_signal_type)
>+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
>+ *	(unsigned int)
>+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
>+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
>+ *	(enum dpll_pin_mode)
>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>+ * @DPLLA_CHANGE_TYPE - type of device change event
>+ *	(enum dpll_change_type)
>+ **/
>+enum dplla {
>+	DPLLA_UNSPEC,
>+	DPLLA_ID,
>+	DPLLA_NAME,
>+	DPLLA_MODE,
>+	DPLLA_MODE_SUPPORTED,
>+	DPLLA_SOURCE_PIN_IDX,
>+	DPLLA_LOCK_STATUS,
>+	DPLLA_TEMP,
>+	DPLLA_CLOCK_ID,
>+	DPLLA_CLOCK_CLASS,
>+	DPLLA_FILTER,
>+	DPLLA_PIN,
>+	DPLLA_PIN_IDX,
>+	DPLLA_PIN_DESCRIPTION,
>+	DPLLA_PIN_TYPE,
>+	DPLLA_PIN_SIGNAL_TYPE,
>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>+	DPLLA_PIN_CUSTOM_FREQ,
>+	DPLLA_PIN_MODE,
>+	DPLLA_PIN_MODE_SUPPORTED,
>+	DPLLA_PIN_PRIO,
>+	DPLLA_PIN_PARENT_IDX,
>+	DPLLA_PIN_NETIFINDEX,

I believe we cannot have this right now. The problem is, ifindexes may
overlay between namespaces. So ifindex without namespace means nothing.
I don't see how this can work from the dpll side.

Lets assign dpll_pin pointer to netdev and expose it over RT netlink in
a similar way devlink_port is exposed. That should be enough for the
user to find a dpll instance for given netdev.

It does not have to be part of this set strictly, but I would like to
have it here, so the full picture could be seen.



>+	DPLLA_CHANGE_TYPE,
>+	__DPLLA_MAX,
>+};
>+
>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>+
>+/* dpll_lock_status - DPLL status provides information of device status
>+ *
>+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
>+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid (or is in
>+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
>+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid signal
>+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
>+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a valid lock
>+ *	or was forced by DPLL_MODE_HOLDOVER mode)
>+ **/
>+enum dpll_lock_status {
>+	DPLL_LOCK_STATUS_UNSPEC,
>+	DPLL_LOCK_STATUS_UNLOCKED,
>+	DPLL_LOCK_STATUS_CALIBRATING,
>+	DPLL_LOCK_STATUS_LOCKED,
>+	DPLL_LOCK_STATUS_HOLDOVER,
>+
>+	__DPLL_LOCK_STATUS_MAX,
>+};
>+
>+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
>+
>+/* dpll_pin_type - signal types
>+ *
>+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
>+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
>+ * @DPLL_PIN_TYPE_EXT - external source
>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
>+ **/
>+enum dpll_pin_type {
>+	DPLL_PIN_TYPE_UNSPEC,
>+	DPLL_PIN_TYPE_MUX,
>+	DPLL_PIN_TYPE_EXT,
>+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>+	DPLL_PIN_TYPE_INT_OSCILLATOR,
>+	DPLL_PIN_TYPE_GNSS,
>+
>+	__DPLL_PIN_TYPE_MAX,
>+};
>+
>+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
>+
>+/* dpll_pin_signal_type - signal types
>+ *
>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal

Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
with HZ value directly? For example, supported freq:
1, 10000000
or:
1, 1000

freq set 10000000
freq set 1

Simple and easy.


>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value defined
>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>+ **/
>+enum dpll_pin_signal_type {
>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>+
>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>+};
>+
>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>+
>+/* dpll_pin_mode - available pin states
>+ *
>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>+ **/
>+enum dpll_pin_mode {
>+	DPLL_PIN_MODE_UNSPEC,
>+	DPLL_PIN_MODE_CONNECTED,
>+	DPLL_PIN_MODE_DISCONNECTED,
>+	DPLL_PIN_MODE_SOURCE,
>+	DPLL_PIN_MODE_OUTPUT,

I don't follow. I see 2 enums:
CONNECTED/DISCONNECTED
SOURCE/OUTPUT
why this is mangled together? How is it supposed to be working. Like a
bitarray?


>+
>+	__DPLL_PIN_MODE_MAX,
>+};
>+
>+#define DPLL_PIN_MODE_MAX (__DPLL_PIN_MODE_MAX - 1)
>+
>+/**
>+ * dpll_event - Events of dpll generic netlink family
>+ *
>+ * @DPLL_EVENT_UNSPEC - invalid event type
>+ * @DPLL_EVENT_DEVICE_CREATE - dpll device created
>+ * @DPLL_EVENT_DEVICE_DELETE - dpll device deleted
>+ * @DPLL_EVENT_DEVICE_CHANGE - attribute of dpll device or pin changed
>+ **/
>+enum dpll_event {
>+	DPLL_EVENT_UNSPEC,
>+	DPLL_EVENT_DEVICE_CREATE,
>+	DPLL_EVENT_DEVICE_DELETE,
>+	DPLL_EVENT_DEVICE_CHANGE,
>+
>+	__DPLL_EVENT_MAX,
>+};
>+
>+#define DPLL_EVENT_MAX (__DPLL_EVENT_MAX - 1)
>+
>+/**
>+ * dpll_change_type - values of events in case of device change event
>+ * (DPLL_EVENT_DEVICE_CHANGE)
>+ *
>+ * @DPLL_CHANGE_UNSPEC - invalid event type
>+ * @DPLL_CHANGE_MODE - mode changed
>+ * @DPLL_CHANGE_LOCK_STATUS - lock status changed
>+ * @DPLL_CHANGE_SOURCE_PIN - source pin changed,

Why comma at the end? Same to couple of others


>+ * @DPLL_CHANGE_TEMP - temperature changed
>+ * @DPLL_CHANGE_PIN_ADD - source pin added,
>+ * @DPLL_CHANGE_PIN_DEL - source pin deleted,
>+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
>+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
>+ * @DPLL_CHANGE_PIN_MODE - pin state changed
>+ * @DPLL_CHANGE_PIN_PRIO - pin prio changed
>+ **/
>+enum dpll_event_change {
>+	DPLL_CHANGE_UNSPEC,
>+	DPLL_CHANGE_MODE,
>+	DPLL_CHANGE_LOCK_STATUS,
>+	DPLL_CHANGE_SOURCE_PIN,
>+	DPLL_CHANGE_TEMP,
>+	DPLL_CHANGE_PIN_ADD,
>+	DPLL_CHANGE_PIN_DEL,
>+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
>+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
>+	DPLL_CHANGE_PIN_MODE,
>+	DPLL_CHANGE_PIN_PRIO,
>+
>+	__DPLL_CHANGE_MAX,
>+};
>+
>+#define DPLL_CHANGE_MAX (__DPLL_CHANGE_MAX - 1)
>+
>+/**
>+ * dpll_cmd - Commands supported by the dpll generic netlink family
>+ *
>+ * @DPLL_CMD_UNSPEC - invalid message type
>+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes of
>+ *	single dpll device and it's pins
>+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
>+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
>+ **/
>+enum dpll_cmd {
>+	DPLL_CMD_UNSPEC,
>+	DPLL_CMD_DEVICE_GET,
>+	DPLL_CMD_DEVICE_SET,
>+	DPLL_CMD_PIN_SET,

Have pin get to get list of pins, then you can have 1:1 mapping to
events and loose the enum dpll_event_change. This is the usual way to do
stuff. Events have the same cmd and message format as get.


>+
>+	__DPLL_CMD_MAX,
>+};
>+
>+#define DPLL_CMD_MAX (__DPLL_CMD_MAX - 1)
>+
>+/**
>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate how
>+ * dpll selects one of its sources to syntonize with a source.
>+ *
>+ * @DPLL_MODE_UNSPEC - invalid
>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request to dpll
>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by dpll
>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator

Why does the user care which oscilator is run internally. It's freerun,
isn't it? If you want to expose oscilator type, you should do it
elsewhere.


>+ **/
>+enum dpll_mode {
>+	DPLL_MODE_UNSPEC,
>+	DPLL_MODE_MANUAL,
>+	DPLL_MODE_AUTOMATIC,
>+	DPLL_MODE_HOLDOVER,
>+	DPLL_MODE_FREERUN,
>+	DPLL_MODE_NCO,
>+
>+	__DPLL_MODE_MAX,
>+};
>+
>+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
>+
>+/**
>+ * dpll_clock_class - enumerate quality class of a DPLL clock as specified in
>+ * Recommendation ITU-T G.8273.2/Y.1368.2.
>+ */
>+enum dpll_clock_class {
>+	DPLL_CLOCK_CLASS_UNSPEC,
>+	DPLL_CLOCK_CLASS_A,
>+	DPLL_CLOCK_CLASS_B,
>+	DPLL_CLOCK_CLASS_C,
>+
>+	__DPLL_CLOCK_CLASS_MAX,
>+};
>+
>+#define DPLL_CLOCK_CLASS_MAX (__DPLL_CLOCK_CLASS_MAX - 1)
>+
>+/**
>+ * enum dpll_type - type of dpll, integer value of enum is embedded into
>+ * name of DPLL device (DPLLA_NAME)

Yeah, I really cannot understand why you think for a second that
embedding an enum value into a name makes sense in this world :O


>+ *
>+ * @DPLL_TYPE_UNSPEC - unspecified
>+ * @DPLL_TYPE_PPS - dpll produces Pulse-Per-Second signal
>+ * @DPLL_TYPE_EEC - dpll drives the Ethernet Equipment Clock
>+ */
>+enum dpll_type {
>+	DPLL_TYPE_UNSPEC,
>+	DPLL_TYPE_PPS,
>+	DPLL_TYPE_EEC,
>+
>+	__DPLL_TYPE_MAX
>+};
>+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
>+
>+#endif /* _UAPI_LINUX_DPLL_H */
>-- 
>2.30.2
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* RE: [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
  2023-01-19  0:15     ` Jakub Kicinski
@ 2023-01-19 17:23       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-19 17:23 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk

>-----Original Message-----
>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, January 19, 2023 1:15 AM
>
>On Wed, 18 Jan 2023 18:07:53 +0000 Kubalewski, Arkadiusz wrote:
>> Based on today's sync meeting, changes we are going to introduce in next
>> version:
>> - reduce the muxed-pin number (artificial multiplication) on list of
>dpll's
>> pins, have a single pin which can be connected with multiple parents,
>> - introduce separated get command for the pin attributes,
>> - allow infinite name length of dpll device,
>> - remove a type embedded in dpll's name and introduce new attribute
>instead,
>> - remove clock class attribute as it is not known by the driver without
>> compliance testing on given SW/HW configuration,
>> - add dpll device "default" quality level attribute, as shall be known
>> by driver for a given hardware.
>
>I converted the patches to use the spec, and pushed them out here:
>
>https://github.com/kuba-moo/ynl/tree/dpll
>
>I kept the conversion step-by-step to help the readers a bit but
>the conversion patches should all be squashed into the main DPLL patch.
>
>The patches are on top of my YNL branch ('main' in that repo).
>I'll post the YNL patches later today, so hopefully very soon they will
>be upstream.
>
>Two major pieces of work which I didn't do for DPLL:
> - docs - I dropped most of the kdocs, the copy-n-pasting was too much;
>   if you want to keep the docs in the uAPI you need to add the
>   appropriate stuff in the spec (look at the definition of
>   pin-signal-type for an example of a fully documented enum)
> - the notifications are quite unorthodox in the current
>   implementation, so I faked the enums :S
>   Usually the notification is the same as the response to a get.
>   IIRC 'notify' and 'event' operation types should be used in the spec.
>
>There is documentation on the specs in
>Documentation/userspace-api/netlink/ which should give some idea of how
>things work. There is also another example of a spec here:
>https://github.com/kuba-
>moo/ynl/blob/psp/Documentation/netlink/specs/psp.yaml
>
>To regenerate the C code after changes to YAML:
>
>  ./tools/net/ynl/ynl-regen.sh
>
>if the Python script doing the generation dies and eats the files -
>bring them back with:
>
>  git checkout drivers/dpll/dpll_nl.c drivers/dpll/dpll_nl.h \
>               include/uapi/linux/dpll.h
>
>There is a "universal CLI" script in:
>
>  ./tools/net/ynl/samples/cli.py
>
>which should be able to take in JSON requests and output JSON responses.
>I'm improvising, because I don't have any implementation to try it
>out, but something like:
>
>  ./tools/net/ynl/samples/cli.py \
>       --spec Documentation/netlink/specs/dpll.yaml \
>       --do device-get --json '{"id": 1}'
>
>should pretty print the info about device with id 1. Actually - it
>probably won't because I didn't fill in all the attrs in the pin nest.
>But with a bit more work on the spec it should work.
>
>Would you be able to finish this conversion. Just LMK if you have any
>problems, the edges are definitely very sharp at this point.

Sure, I will try. Thanks for the manual!

BR, Arkadiusz

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

* RE: [RFC PATCH v5 0/4] Create common DPLL/clock configuration API
@ 2023-01-19 17:23       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-19 17:23 UTC (permalink / raw)
  To: Jakub Kicinski, Vadim Fedorenko
  Cc: Jiri Pirko, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk

>-----Original Message-----
>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, January 19, 2023 1:15 AM
>
>On Wed, 18 Jan 2023 18:07:53 +0000 Kubalewski, Arkadiusz wrote:
>> Based on today's sync meeting, changes we are going to introduce in next
>> version:
>> - reduce the muxed-pin number (artificial multiplication) on list of
>dpll's
>> pins, have a single pin which can be connected with multiple parents,
>> - introduce separated get command for the pin attributes,
>> - allow infinite name length of dpll device,
>> - remove a type embedded in dpll's name and introduce new attribute
>instead,
>> - remove clock class attribute as it is not known by the driver without
>> compliance testing on given SW/HW configuration,
>> - add dpll device "default" quality level attribute, as shall be known
>> by driver for a given hardware.
>
>I converted the patches to use the spec, and pushed them out here:
>
>https://github.com/kuba-moo/ynl/tree/dpll
>
>I kept the conversion step-by-step to help the readers a bit but
>the conversion patches should all be squashed into the main DPLL patch.
>
>The patches are on top of my YNL branch ('main' in that repo).
>I'll post the YNL patches later today, so hopefully very soon they will
>be upstream.
>
>Two major pieces of work which I didn't do for DPLL:
> - docs - I dropped most of the kdocs, the copy-n-pasting was too much;
>   if you want to keep the docs in the uAPI you need to add the
>   appropriate stuff in the spec (look at the definition of
>   pin-signal-type for an example of a fully documented enum)
> - the notifications are quite unorthodox in the current
>   implementation, so I faked the enums :S
>   Usually the notification is the same as the response to a get.
>   IIRC 'notify' and 'event' operation types should be used in the spec.
>
>There is documentation on the specs in
>Documentation/userspace-api/netlink/ which should give some idea of how
>things work. There is also another example of a spec here:
>https://github.com/kuba-
>moo/ynl/blob/psp/Documentation/netlink/specs/psp.yaml
>
>To regenerate the C code after changes to YAML:
>
>  ./tools/net/ynl/ynl-regen.sh
>
>if the Python script doing the generation dies and eats the files -
>bring them back with:
>
>  git checkout drivers/dpll/dpll_nl.c drivers/dpll/dpll_nl.h \
>               include/uapi/linux/dpll.h
>
>There is a "universal CLI" script in:
>
>  ./tools/net/ynl/samples/cli.py
>
>which should be able to take in JSON requests and output JSON responses.
>I'm improvising, because I don't have any implementation to try it
>out, but something like:
>
>  ./tools/net/ynl/samples/cli.py \
>       --spec Documentation/netlink/specs/dpll.yaml \
>       --do device-get --json '{"id": 1}'
>
>should pretty print the info about device with id 1. Actually - it
>probably won't because I didn't fill in all the attrs in the pin nest.
>But with a bit more work on the spec it should work.
>
>Would you be able to finish this conversion. Just LMK if you have any
>problems, the edges are definitely very sharp at this point.

Sure, I will try. Thanks for the manual!

BR, Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-01-19 17:16     ` Jiri Pirko
@ 2023-01-20 12:56       ` Jiri Pirko
  -1 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-20 12:56 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk, Milena Olech,
	Michal Michalik

Thu, Jan 19, 2023 at 06:16:13PM CET, jiri@resnulli.us wrote:
>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:

[...]


>>+/**
>>+ * dpll_cmd - Commands supported by the dpll generic netlink family
>>+ *
>>+ * @DPLL_CMD_UNSPEC - invalid message type
>>+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes of
>>+ *	single dpll device and it's pins
>>+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
>>+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
>>+ **/
>>+enum dpll_cmd {
>>+	DPLL_CMD_UNSPEC,
>>+	DPLL_CMD_DEVICE_GET,
>>+	DPLL_CMD_DEVICE_SET,
>>+	DPLL_CMD_PIN_SET,
>
>Have pin get to get list of pins, then you can have 1:1 mapping to
>events and loose the enum dpll_event_change. This is the usual way to do
>stuff. Events have the same cmd and message format as get.

I was thinking about that a bit more.
1) There is 1:n relationship between PIN and DPLL(s).
2) The pin configuration is independent on DPLL, with an
   exeption of PRIO.

Therefore as I suggested in the reply to this patch, the pin should be
separate entity, allocated and having ops unrelated to DPLL. It is just
registered to the DPLLs that are using the pin.

The pin ops should not have dpll pointer as arg, again with exception of
PRIO.

DPLL_CMD_DEVICE_GET should not contain pins at all.

There should be DPLL_CMD_PIN_GET added which can dump and will be used
to get the list of pins in the system.
- if DPLL handle is passed to DPLL_CMD_PIN_GET, it will dump only pins
  related to the specified DPLL.

DPLL_CMD_PIN_GET message will contain pin-specific attrs and will have a
list of connected DPLLs:
       DPLLA_PIN_IDX
       DPLLA_PIN_DESCRIPTION
       DPLLA_PIN_TYPE
       DPLLA_PIN_SIGNAL_TYPE
       DPLLA_PIN_SIGNAL_TYPE_SUPPORTED
       DPLLA_PIN_CUSTOM_FREQ
       DPLLA_PIN_MODE
       DPLLA_PIN_MODE_SUPPORTED
       DPLLA_PIN_PARENT_IDX
       DPLLA_PIN_DPLL    (nested)
          DPLLA_DPLL_HANDLE   "dpll_0"
          DPLLA_PIN_PRIO    1
       DPLLA_PIN_DPLL    (nested)
          DPLLA_DPLL_HANDLE   "dpll_1"
          DPLLA_PIN_PRIO    2

Please take the names lightly. My point is to show 2 nests for 2
DPLLS connected, on each the pin has different prio.

Does this make sense?

One issue to be solved is the pin indexing. As pin would be separate
entity, the indexing would be global and therefore not predictable. We
would have to figure it out differntly. Pehaps something like this:

$ dpll dev show
pci/0000:08:00.0: dpll 1             first dpll on 0000:08:00.0
pci/0000:08:00.0: dpll 2             second dpll on the same pci device
pci/0000:09:00.0: dpll 1             first dpll on 0000:09:00.0
pci/0000:09:00.0: dpll 2             second dpll on the same pci device

$ dpll pin show
pci/0000:08:00.0: pin 1 desc SOMELABEL_A
  dpll 1:                          This refers to DPLL 1 on the same pci device
    prio 80
  dpll 2:                          This refers to DPLL 2 on the same pci device
    prio 100
pci/0000:08:00.0: pin 2 desc SOMELABEL_B
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:08:00.0: pin 3 desc SOMELABEL_C
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:08:00.0: pin 4 desc SOMELABEL_D
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:09:00.0: pin 1 desc SOMEOTHERLABEL_A
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:09:00.0: pin 2 desc SOMEOTHERLABEL_B
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:09:00.0: pin 3 desc SOMEOTHERLABEL_C
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:09:00.0: pin 4 desc SOMEOTHERLABEL_C
  dpll 1:
    prio 80
  dpll 2:
    prio 100

Note there are 2 groups of pins, one for each pci device.

Setting some attribute command would looks like:
To set DPLL mode:
$ dpll dev set pci/0000:08:00.0 dpll 1 mode freerun
   netlink:
   DPLL_CMD_DEVICE_SET
      DPLLA_BUS_NAME "pci"
      DPLLA_DEV_NAME "0000:08:00.0"
      DPLLA_DPLL_INDEX 1
      DPLLA_DPLL_MODE 3

$ dpll dev set pci/0000:08:00.0 dpll 2 mode automatic


To set signal frequency in HZ:
$ dpll pin set pci/0000:08:00.0 pin 3 frequency 10000000
   netlink:
   DPLL_CMD_PIN_SET
      DPLLA_BUS_NAME "pci"
      DPLLA_DEV_NAME "0000:08:00.0"
      DPLLA_PIN_INDEX 3
      DPLLA_PIN_FREQUENCY 10000000

$ dpll pin set pci/0000:08:00.0 pin 1 frequency 1


To set individual of one pin for 2 DPLLs:
$ dpll pin set pci/0000:08:00.0 pin 1 dpll 1 prio 40
   netlink:
   DPLL_CMD_PIN_SET
      DPLLA_BUS_NAME "pci"
      DPLLA_DEV_NAME "0000:08:00.0"
      DPLLA_PIN_INDEX 1
      DPLLA_DPLL_INDEX 1
      DPLLA_PIN_PRIO 40

$ dpll pin set pci/0000:08:00.0 pin 1 dpll 2 prio 80


Isn't this neat?


[...]

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-01-20 12:56       ` Jiri Pirko
  0 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-20 12:56 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk, Milena Olech,
	Michal Michalik

Thu, Jan 19, 2023 at 06:16:13PM CET, jiri@resnulli.us wrote:
>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:

[...]


>>+/**
>>+ * dpll_cmd - Commands supported by the dpll generic netlink family
>>+ *
>>+ * @DPLL_CMD_UNSPEC - invalid message type
>>+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes of
>>+ *	single dpll device and it's pins
>>+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
>>+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
>>+ **/
>>+enum dpll_cmd {
>>+	DPLL_CMD_UNSPEC,
>>+	DPLL_CMD_DEVICE_GET,
>>+	DPLL_CMD_DEVICE_SET,
>>+	DPLL_CMD_PIN_SET,
>
>Have pin get to get list of pins, then you can have 1:1 mapping to
>events and loose the enum dpll_event_change. This is the usual way to do
>stuff. Events have the same cmd and message format as get.

I was thinking about that a bit more.
1) There is 1:n relationship between PIN and DPLL(s).
2) The pin configuration is independent on DPLL, with an
   exeption of PRIO.

Therefore as I suggested in the reply to this patch, the pin should be
separate entity, allocated and having ops unrelated to DPLL. It is just
registered to the DPLLs that are using the pin.

The pin ops should not have dpll pointer as arg, again with exception of
PRIO.

DPLL_CMD_DEVICE_GET should not contain pins at all.

There should be DPLL_CMD_PIN_GET added which can dump and will be used
to get the list of pins in the system.
- if DPLL handle is passed to DPLL_CMD_PIN_GET, it will dump only pins
  related to the specified DPLL.

DPLL_CMD_PIN_GET message will contain pin-specific attrs and will have a
list of connected DPLLs:
       DPLLA_PIN_IDX
       DPLLA_PIN_DESCRIPTION
       DPLLA_PIN_TYPE
       DPLLA_PIN_SIGNAL_TYPE
       DPLLA_PIN_SIGNAL_TYPE_SUPPORTED
       DPLLA_PIN_CUSTOM_FREQ
       DPLLA_PIN_MODE
       DPLLA_PIN_MODE_SUPPORTED
       DPLLA_PIN_PARENT_IDX
       DPLLA_PIN_DPLL    (nested)
          DPLLA_DPLL_HANDLE   "dpll_0"
          DPLLA_PIN_PRIO    1
       DPLLA_PIN_DPLL    (nested)
          DPLLA_DPLL_HANDLE   "dpll_1"
          DPLLA_PIN_PRIO    2

Please take the names lightly. My point is to show 2 nests for 2
DPLLS connected, on each the pin has different prio.

Does this make sense?

One issue to be solved is the pin indexing. As pin would be separate
entity, the indexing would be global and therefore not predictable. We
would have to figure it out differntly. Pehaps something like this:

$ dpll dev show
pci/0000:08:00.0: dpll 1             first dpll on 0000:08:00.0
pci/0000:08:00.0: dpll 2             second dpll on the same pci device
pci/0000:09:00.0: dpll 1             first dpll on 0000:09:00.0
pci/0000:09:00.0: dpll 2             second dpll on the same pci device

$ dpll pin show
pci/0000:08:00.0: pin 1 desc SOMELABEL_A
  dpll 1:                          This refers to DPLL 1 on the same pci device
    prio 80
  dpll 2:                          This refers to DPLL 2 on the same pci device
    prio 100
pci/0000:08:00.0: pin 2 desc SOMELABEL_B
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:08:00.0: pin 3 desc SOMELABEL_C
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:08:00.0: pin 4 desc SOMELABEL_D
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:09:00.0: pin 1 desc SOMEOTHERLABEL_A
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:09:00.0: pin 2 desc SOMEOTHERLABEL_B
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:09:00.0: pin 3 desc SOMEOTHERLABEL_C
  dpll 1:
    prio 80
  dpll 2:
    prio 100
pci/0000:09:00.0: pin 4 desc SOMEOTHERLABEL_C
  dpll 1:
    prio 80
  dpll 2:
    prio 100

Note there are 2 groups of pins, one for each pci device.

Setting some attribute command would looks like:
To set DPLL mode:
$ dpll dev set pci/0000:08:00.0 dpll 1 mode freerun
   netlink:
   DPLL_CMD_DEVICE_SET
      DPLLA_BUS_NAME "pci"
      DPLLA_DEV_NAME "0000:08:00.0"
      DPLLA_DPLL_INDEX 1
      DPLLA_DPLL_MODE 3

$ dpll dev set pci/0000:08:00.0 dpll 2 mode automatic


To set signal frequency in HZ:
$ dpll pin set pci/0000:08:00.0 pin 3 frequency 10000000
   netlink:
   DPLL_CMD_PIN_SET
      DPLLA_BUS_NAME "pci"
      DPLLA_DEV_NAME "0000:08:00.0"
      DPLLA_PIN_INDEX 3
      DPLLA_PIN_FREQUENCY 10000000

$ dpll pin set pci/0000:08:00.0 pin 1 frequency 1


To set individual of one pin for 2 DPLLs:
$ dpll pin set pci/0000:08:00.0 pin 1 dpll 1 prio 40
   netlink:
   DPLL_CMD_PIN_SET
      DPLLA_BUS_NAME "pci"
      DPLLA_DEV_NAME "0000:08:00.0"
      DPLLA_PIN_INDEX 1
      DPLLA_DPLL_INDEX 1
      DPLLA_PIN_PRIO 40

$ dpll pin set pci/0000:08:00.0 pin 1 dpll 2 prio 80


Isn't this neat?


[...]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* RE: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-01-19 17:16     ` Jiri Pirko
@ 2023-01-27 18:12       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-27 18:12 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, January 19, 2023 6:16 PM
>
>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>>DPLL framework is used to represent and configure DPLL devices
>>in systems. Each device that has DPLL and can configure sources
>>and outputs can use this framework. Netlink interface is used to
>>provide configuration data and to receive notification messages
>>about changes in the configuration or status of DPLL device.
>>Inputs and outputs of the DPLL device are represented as special
>>objects which could be dynamically added to and removed from DPLL
>>device.
>>
>>Co-developed-by: Milena Olech <milena.olech@intel.com>
>>Signed-off-by: Milena Olech <milena.olech@intel.com>
>>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
>>---
>> MAINTAINERS                 |    8 +
>> drivers/Kconfig             |    2 +
>> drivers/Makefile            |    1 +
>> drivers/dpll/Kconfig        |    7 +
>> drivers/dpll/Makefile       |    9 +
>> drivers/dpll/dpll_core.c    | 1010 +++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h    |  105 ++++
>> drivers/dpll/dpll_netlink.c |  883 ++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h |   24 +
>> include/linux/dpll.h        |  282 ++++++++++
>> include/uapi/linux/dpll.h   |  294 ++++++++++
>> 11 files changed, 2625 insertions(+)
>> create mode 100644 drivers/dpll/Kconfig
>> create mode 100644 drivers/dpll/Makefile
>> create mode 100644 drivers/dpll/dpll_core.c
>> create mode 100644 drivers/dpll/dpll_core.h
>> create mode 100644 drivers/dpll/dpll_netlink.c
>> create mode 100644 drivers/dpll/dpll_netlink.h
>> create mode 100644 include/linux/dpll.h
>> create mode 100644 include/uapi/linux/dpll.h
>>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index f82dd8d43c2b..de8a10b21ce8 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -6411,6 +6411,14 @@ F:
>>	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/swit
>>ch-drive
>> F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
>> F:	drivers/net/ethernet/freescale/dpaa2/dpsw*
>>
>>+DPLL CLOCK SUBSYSTEM
>>+M:	Vadim Fedorenko <vadfed@fb.com>
>>+L:	netdev@vger.kernel.org
>>+S:	Maintained
>>+F:	drivers/dpll/*
>>+F:	include/net/dpll.h
>>+F:	include/uapi/linux/dpll.h
>>+
>> DRBD DRIVER
>> M:	Philipp Reisner <philipp.reisner@linbit.com>
>> M:	Lars Ellenberg <lars.ellenberg@linbit.com>
>>diff --git a/drivers/Kconfig b/drivers/Kconfig
>>index 968bd0a6fd78..453df9e1210d 100644
>>--- a/drivers/Kconfig
>>+++ b/drivers/Kconfig
>>@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
>>
>> source "drivers/hte/Kconfig"
>>
>>+source "drivers/dpll/Kconfig"
>>+
>> endmenu
>>diff --git a/drivers/Makefile b/drivers/Makefile
>>index bdf1c66141c9..7cbee58bc692 100644
>>--- a/drivers/Makefile
>>+++ b/drivers/Makefile
>>@@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER)		+= counter/
>> obj-$(CONFIG_MOST)		+= most/
>> obj-$(CONFIG_PECI)		+= peci/
>> obj-$(CONFIG_HTE)		+= hte/
>>+obj-$(CONFIG_DPLL)		+= dpll/
>>diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
>>new file mode 100644
>>index 000000000000..a4cae73f20d3
>>--- /dev/null
>>+++ b/drivers/dpll/Kconfig
>>@@ -0,0 +1,7 @@
>>+# SPDX-License-Identifier: GPL-2.0-only
>>+#
>>+# Generic DPLL drivers configuration
>>+#
>>+
>>+config DPLL
>>+  bool
>>diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>new file mode 100644
>>index 000000000000..b18cf848a010
>>--- /dev/null
>>+++ b/drivers/dpll/Makefile
>>@@ -0,0 +1,9 @@
>>+# SPDX-License-Identifier: GPL-2.0
>>+#
>>+# Makefile for DPLL drivers.
>>+#
>>+
>>+obj-$(CONFIG_DPLL)          += dpll_sys.o
>>+dpll_sys-y                  += dpll_core.o
>>+dpll_sys-y                  += dpll_netlink.o
>>+
>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>new file mode 100644
>>index 000000000000..fec534f17827
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.c
>>@@ -0,0 +1,1010 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ *  dpll_core.c - Generic DPLL Management class support.
>>+ *
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>+
>>+#include <linux/device.h>
>>+#include <linux/err.h>
>>+#include <linux/slab.h>
>>+#include <linux/string.h>
>>+
>>+#include "dpll_core.h"
>>+
>>+/**
>>+ * struct dpll_pin - structure for a dpll pin
>>+ * @idx:		unique id number for each pin
>>+ * @parent_pin:		parent pin
>>+ * @type:		type of the pin
>>+ * @ops:		operations this &dpll_pin supports
>>+ * @priv:		pointer to private information of owner
>>+ * @ref_dplls:		array of registered dplls
>>+ * @description:	name to distinguish the pin
>>+ */
>>+struct dpll_pin {
>>+	u32 idx;
>>+	struct dpll_pin *parent_pin;
>>+	enum dpll_pin_type type;
>>+	struct dpll_pin_ops *ops;
>>+	void *priv;
>>+	struct xarray ref_dplls;
>>+	char description[DPLL_PIN_DESC_LEN];
>>+};
>>+static DEFINE_MUTEX(dpll_device_xa_lock);
>>+
>>+static DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
>>+#define DPLL_REGISTERED		XA_MARK_1
>>+#define PIN_REGISTERED		XA_MARK_1
>
>DPLL_PIN_REGISTERED
>

Agree, will fix that.

>
>>+
>>+#define ASSERT_DPLL_REGISTERED(d)
>>\
>>+	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>>+#define ASSERT_DPLL_NOT_REGISTERED(d)
>>\
>>+	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>>+
>>+struct dpll_pin_ref {
>>+	struct dpll_device *dpll;
>>+	struct dpll_pin_ops *ops;
>>+	void *priv;
>>+};
>>+
>>+/**
>>+ * dpll_device_get_by_id - find dpll device by it's id
>>+ * @id: id of searched dpll
>>+ *
>>+ * Return: dpll_device struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_device_get_by_id(int id)
>>+{
>>+	struct dpll_device *dpll = NULL;
>>+
>>+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>>+		dpll = xa_load(&dpll_device_xa, id);
>>+
>>+	return dpll;
>>+}
>>+
>>+/**
>>+ * dpll_device_get_by_name - find dpll device by it's id
>>+ * @name: name of searched dpll
>>+ *
>>+ * Return: dpll_device struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_device_get_by_name(const char *name)
>>+{
>>+	struct dpll_device *dpll, *ret = NULL;
>>+	unsigned long index;
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>>+		if (!strcmp(dev_name(&dpll->dev), name)) {
>>+			ret = dpll;
>>+			break;
>>+		}
>>+	}
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+
>>+	return ret;
>>+}
>>+
>>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>
>Hmm, don't you want to put an owner module as an arg here as well? I
>don't see how could 2 modules sanely work with the same dpll instance.
>

Sorry, I don't get it.
How the driver that needs to find a dpll would know the owner module?
The idea of this is to let another driver instance to find a dpll device
already registered in OS.
The driver that is searching dpll device is not the same as the one that has
created the device, otherwise it wouldn't make any sense?

>
>>+						enum dpll_type type, u8 idx)
>>+{
>>+	struct dpll_device *dpll, *ret = NULL;
>>+	unsigned long index;
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>>+		if (dpll->clock_id == clock_id) {
>>+			if (dpll->type == type) {
>>+				if (dpll->dev_driver_idx == idx) {
>>+					ret = dpll;
>>+					break;
>>+				}
>>+			}
>>+		}
>>+	}
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
>>+
>>+static void dpll_device_release(struct device *dev)
>>+{
>>+	struct dpll_device *dpll;
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	dpll = to_dpll_device(dev);
>>+	dpll_device_unregister(dpll);
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+	dpll_device_free(dpll);
>>+}
>>+
>>+static struct class dpll_class = {
>>+	.name = "dpll",
>>+	.dev_release = dpll_device_release,
>
>Why do you want to do this? Why the driver cannot do
>dpll_device_unregister/free() manually. I think it makes things easier
>to read then to rely on dev garbage collector.
>

This was in the first version submitted by Vadim.
I think we can remove it, unless someone has different view?

>
>>+};
>>+
>>+struct dpll_device
>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>>+		   u8 dev_driver_idx, void *priv, struct device *parent)
>>+{
>>+	struct dpll_device *dpll;
>>+	int ret;
>>+
>>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>>+	if (!dpll)
>>+		return ERR_PTR(-ENOMEM);
>>+
>>+	mutex_init(&dpll->lock);
>>+	dpll->ops = ops;
>>+	dpll->dev.class = &dpll_class;
>>+	dpll->parent = parent;
>>+	dpll->type = type;
>>+	dpll->dev_driver_idx = dev_driver_idx;
>>+	dpll->clock_id = clock_id;
>>+	dpll->clock_class = clock_class;
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>>+		       xa_limit_16b, GFP_KERNEL);
>>+	if (ret)
>>+		goto error;
>>+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
>>+		     dev_driver_idx);
>>+	dpll->priv = priv;
>>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>
>What is exactly the point of using this mark?
>

I think this can be also removed now, as there is no separated alloc/register
for newly created dpll device.

>
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+	dpll_notify_device_create(dpll);
>>+
>>+	return dpll;
>>+
>>+error:
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+	kfree(dpll);
>>+	return ERR_PTR(ret);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_alloc);
>>+
>>+void dpll_device_free(struct dpll_device *dpll)
>>+{
>>+	WARN_ON_ONCE(!dpll);
>>+	WARN_ON_ONCE(!xa_empty(&dpll->pins));
>>+	xa_destroy(&dpll->pins);
>>+	mutex_destroy(&dpll->lock);
>>+	kfree(dpll);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_free);
>>+
>>+/**
>>+ * dpll_device_unregister - unregister dpll device
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Note: It does not free the memory
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll)
>>+{
>>+	ASSERT_DPLL_REGISTERED(dpll);
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	xa_erase(&dpll_device_xa, dpll->id);
>>+	dpll_notify_device_delete(dpll);
>
>Why do you need to hold the lock for notify?
>

Good catch, will move it out of critical section.

>
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_unregister);
>>+
>>+/**
>>+ * dpll_id - return dpll id
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: dpll id.
>>+ */
>>+u32 dpll_id(struct dpll_device *dpll)
>>+{
>>+	return dpll->id;
>>+}
>>+
>>+/**
>>+ * dpll_pin_idx - return index of a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Return: index of a pin or PIN_IDX_INVALID if not found.
>>+ */
>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)
>>+{
>>+	struct dpll_pin *pos;
>>+	unsigned long index;
>>+
>>+	xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED) {
>>+		if (pos == pin)
>
>What is the purpose of the lookup for the pin struct you pass as an arg?
>

Seems like no longer needed, will try to remove it.

>
>>+			return pin->idx;
>>+	}
>>+
>>+	return PIN_IDX_INVALID;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_idx);
>>+
>>+const char *dpll_dev_name(struct dpll_device *dpll)
>>+{
>>+	return dev_name(&dpll->dev);
>>+}
>>+
>>+struct dpll_pin *dpll_pin_alloc(const char *description,
>>+				const enum dpll_pin_type pin_type)
>
>s/pin_type/type/
>

Sure, will do.

>
>>+{
>>+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
>>+
>>+	if (!pin)
>>+		return ERR_PTR(-ENOMEM);
>>+	if (pin_type <= DPLL_PIN_TYPE_UNSPEC ||
>>+	    pin_type > DPLL_PIN_TYPE_MAX)
>>+		return ERR_PTR(-EINVAL);
>
>I think this check is not needed here. If driver is passing something
>else, it is buggy. Idk. If you decide to leave this, put it in WARN_ON
>

Sure, will do.

>
>>+
>>+	strncpy(pin->description, description, DPLL_PIN_DESC_LEN);
>
>kstrdup. Please treat the rest of the strings like that. No need to
>limit the string names.

Ok, will follow.

>
>
>>+	pin->description[DPLL_PIN_DESC_LEN - 1] = '\0';
>>+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
>>+	pin->type = pin_type;
>>+
>>+	return pin;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_alloc);
>>+
>>+static int dpll_alloc_pin_on_xa(struct xarray *pins, struct dpll_pin
>>*pin)
>>+{
>>+	struct dpll_pin *pos;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each(pins, index, pos) {
>>+		if (pos == pin ||
>>+		    !strncmp(pos->description, pin->description,
>>+			     DPLL_PIN_DESC_LEN))
>
>WARN_ON. The driver is buggy if it does something like this.

Sure, will do.

>
>
>>+			return -EEXIST;
>>+	}
>>+
>>+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
>>+	if (!ret)
>>+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);
>
>What is exactly the point of having this mark?
>

Think this could be now removed, we got rid of separated alloc/register for
dpll device.

>
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device
>>*dpll,
>>+			    struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	struct dpll_pin_ref *ref, *pos;
>>+	unsigned long index;
>>+	u32 idx;
>>+
>>+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
>>+	if (!ref)
>>+		return -ENOMEM;
>>+	ref->dpll = dpll;
>>+	ref->ops = ops;
>>+	ref->priv = priv;
>>+	if (!xa_empty(&pin->ref_dplls)) {
>
>Pointless check. Just do iterate.
>

Sure, will do.

>
>>+		xa_for_each(&pin->ref_dplls, index, pos) {
>>+			if (pos->dpll == ref->dpll)
>>+				return -EEXIST;
>>+		}
>>+	}
>>+
>>+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b,
>>GFP_KERNEL);
>>+}
>>+
>>+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device
>>*dpll)
>>+{
>>+	struct dpll_pin_ref *pos;
>>+	unsigned long index;
>>+
>>+	xa_for_each(&pin->ref_dplls, index, pos) {
>>+		if (pos->dpll == dpll) {
>>+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
>>+			break;
>>+		}
>>+	}
>>+}
>>+
>>+static int pin_deregister_from_xa(struct xarray *xa_pins, struct dpll_pin
>>*pin)
>
>1) dpll_ prefix

Sure, will do.

>2) "deregister" is odd name

Rodger that, will fix.

>3) why don't you have it next to dpll_alloc_pin_on_xa() as it is a
>   symmetric function?

Will do.

>4) Why exactly just xa_erase() would not do?

Might do, but need to rethink this :)

>
>>+{
>>+	struct dpll_pin *pos;
>>+	unsigned long index;
>>+
>>+	xa_for_each(xa_pins, index, pos) {
>>+		if (pos == pin) {
>>+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));
>
>You have an odd pattern of functions getting pin as an arg then
>doing lookup for the same pin. I have to be missing to see some
>black magic here :O
>

The black magic was done to target correct pin in case pin index differs
between dplls it was registered with. It would depend on the way shared pins
are going to be allocated.
If mixed pins approach is allowed (shared + non-shared pins) on any dpll, we
would end up in situation where pin index for the same physical pin on multiple
devices may be different, depending on registering pins order.

As desribed in below comments, I can see here one simple solution: allow kernel
module (which registers a pin with dpll) to control/assign pin index.
The kernel module would only need take care of them being unique, when
registers with first dpll - which seems not a problem. This way we would also
be albe to get rid of searching pin function (as indexes would be known for all
driver instances), different driver instances would use that index to share a
pin.
Also all the blackmagic like you described wouldn't be needed, thus simplifing
a dpll subsystem.

>
>>+			return 0;
>>+		}
>>+	}
>>+
>>+	return -ENXIO;
>>+}
>>+
>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	int ret;
>>+
>>+	if (!pin || !ops)
>>+		return -EINVAL;
>>+
>>+	mutex_lock(&dpll->lock);
>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>+	if (!ret) {
>>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>>+		if (ret)
>>+			pin_deregister_from_xa(&dpll->pins, pin);
>>+	}
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>+
>>+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins, int
>>idx)
>>+{
>>+	struct dpll_pin *pos;
>>+	unsigned long index;
>>+
>>+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
>>+		if (pos->idx == idx)
>>+			return pos;
>>+	}
>>+
>>+	return NULL;
>>+}
>>+
>>+/**
>>+ * dpll_pin_get_by_idx - find a pin by its index
>>+ * @dpll: dpll device pointer
>>+ * @idx: index of pin
>>+ *
>>+ * Allows multiple driver instances using one physical DPLL to find
>>+ * and share pin already registered with existing dpll device.
>>+ *
>>+ * Return: pointer if pin was found, NULL otherwise.
>>+ */
>>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
>>+{
>>+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
>>+}
>>+
>>+	struct dpll_pin
>>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char
>>*description)
>>+{
>>+	struct dpll_pin *pos, *pin = NULL;
>>+	unsigned long index;
>>+
>>+	xa_for_each(&dpll->pins, index, pos) {
>>+		if (!strncmp(pos->description, description,
>>+			     DPLL_PIN_DESC_LEN)) {
>>+			pin = pos;
>>+			break;
>>+		}
>>+	}
>>+
>>+	return pin;
>>+}
>>+
>>+int
>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>+			 struct dpll_device *dpll,
>>+			 const char *shared_pin_description,
>
>I don't follow why you need to pass the string. You have struct dpll_pin
>* in the driver. Pass that instead, avoid string to refer to kernel
>object. But this is something I wrote multiple times.
>

I wrote this so many times :) Separated driver instances doesn't have the pin
object pointer by default (unless they share it through some unwanted static/
global contatiners). They need to somehow target a pin, right now only unique
attributes on dpll/pin pair are a description and index.
Desription is a constant, index depends on the order of initialization and is
internal for a dpll device.
Previously there were a function to obtain a pin index by its description, then
register with obtained index, now this is merged into one function.

Altough I agree this is still not best aproach.
I will fix by: fallback to targeting a pin to be shared by its index, with one
slight design change, the pin index would have to be given by the driver
instance which registers it with the first dpll.
All the other separated driver instances which are using that pin will have to
know the index assigned to the pin that is going to be shared, which seems
like a best approach to fix this issue.

>
>>+			 struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	struct dpll_pin *pin;
>>+	int ret;
>>+
>>+	mutex_lock(&dpll_pin_owner->lock);
>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>+					  shared_pin_description);
>>+	if (!pin) {
>>+		ret = -EINVAL;
>>+		goto unlock;
>>+	}
>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>+unlock:
>>+	mutex_unlock(&dpll_pin_owner->lock);
>>+
>>+	return ret;
>
>I don't understand why there should be a separate function to register
>the shared pin. As I see it, there is a pin object that could be
>registered with 2 or more dpll devices. What about having:
>
>pin = dpll_pin_alloc(desc, type, ops, priv)
>dpll_pin_register(dpll_1, pin);
>dpll_pin_register(dpll_2, pin);
>dpll_pin_register(dpll_3, pin);
>

IMHO your example works already, but it would possible only if the same driver
instance initializes all dplls.
dpll_shared_pin_register is designed for driver instances without the pin
object.
 
>Then one pin will we in 3 xa_arrays for 3 dplls.
>

As we can see dpll_shared_pin_register is a fancy wrapper for
dpll_pin_register. So yeah, that's the point :) Just separated driver instances
sharing a pin are a issue, will fix with the approach described above (pin
index given by the registering driver instance).

>
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);
>>+
>>+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin)
>
>s/deregister/unregister. Be consistent in naming the functions.
>

Sure, will fix.

>
>>+{
>>+	int ret = 0;
>>+
>>+	if (xa_empty(&dpll->pins))
>>+		return -ENOENT;
>
>Remove this check
>

Sure, can do.

>>+
>>+	mutex_lock(&dpll->lock);
>>+	ret = pin_deregister_from_xa(&dpll->pins, pin);
>>+	if (!ret)
>>+		dpll_pin_ref_del(pin, dpll);
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_DEL);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_deregister);
>>+
>>+void dpll_pin_free(struct dpll_pin *pin)
>>+{
>>+	if (!xa_empty(&pin->ref_dplls))
>>+		return;
>>+
>>+	xa_destroy(&pin->ref_dplls);
>>+	kfree(pin);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_free);
>>+
>>+int dpll_muxed_pin_register(struct dpll_device *dpll,
>>+			    const char *parent_pin_description,
>
>Again, pass struct dpll_pin *parent, not a string.
>
>
>>+			    struct dpll_pin *pin,
>>+			    struct dpll_pin_ops *ops, void *priv)
>

Again, separated driver instances are the issue. Will fix with a parent pin
index given by registering driver, approach described above.

>Why this is a separate function? Why can't we have one function
>say __dpll_pin_register()
>which is called from
>dpll_pin_register() - parent == null
>or
>dpll_muxed_pin_register() - parent == valid parent pointer
>?
>

Sure, can try that. Altough as described above, a pin index instead of parent
pointer.

>
>
>>+{
>>+	struct dpll_pin *parent_pin;
>>+	int ret;
>>+
>>+	if (!parent_pin_description || !pin)
>>+		return -EINVAL;
>>+
>>+	mutex_lock(&dpll->lock);
>>+	parent_pin = dpll_pin_get_by_description(dpll,
>>parent_pin_description);
>>+	if (!parent_pin)
>>+		return -EINVAL;
>>+	if (parent_pin->type != DPLL_PIN_TYPE_MUX)
>>+		return -EPERM;
>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>+	if (!ret)
>>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>>+	if (!ret)
>>+		pin->parent_pin = parent_pin;
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
>>+
>>+/**
>>+ * dpll_pin_first - get first registered pin
>>+ * @dpll: registered dpll pointer
>>+ * @index: found pin index (out)
>>+ *
>>+ * Return: dpll_pin struct if found, NULL otherwise.
>>+ */
>>+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long
>>*index)
>>+{
>>+	*index = 0;
>>+
>>+	return xa_find(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>>+}
>>+
>>+/**
>>+ * dpll_pin_next - get next registered pin to the relative pin
>>+ * @dpll: registered dpll pointer
>>+ * @index: relative pin index (in and out)
>>+ *
>>+ * Return: dpll_pin struct if found, NULL otherwise.
>>+ */
>>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long
>>*index)
>>+{
>>+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>>+}
>>+
>>+/**
>>+ * dpll_first - get first registered dpll device
>>+ * @index: found dpll index (out)
>>+ *
>>+ * Return: dpll_device struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_first(unsigned long *index)
>>+{
>>+	*index = 0;
>>+
>>+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>>+}
>>+
>>+/**
>>+ * dpll_pin_next - get next registered dpll device to the relative pin
>>+ * @index: relative dpll index (in and out)
>>+ *
>>+ * Return: dpll_pin struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_next(unsigned long *index)
>>+{
>>+	return xa_find_after(&dpll_device_xa, index, LONG_MAX,
>>DPLL_REGISTERED);
>>+}
>>+
>>+static struct dpll_pin_ref
>>+*dpll_pin_find_ref(const struct dpll_device *dpll, const struct dpll_pin
>>*pin)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long index;
>>+
>>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>>+		if (ref->dpll != dpll)
>>+			continue;
>>+		else
>>+			return ref;
>>+	}
>>+
>>+	return NULL;
>>+}
>>+
>>+/**
>>+ * dpll_pin_type_get - get type of a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @type: on success - configured pin type
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got pin's type
>>+ * * negative - failed to get pin's type
>>+ */
>>+int dpll_pin_type_get(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin,
>>+		      enum dpll_pin_type *type)
>>+{
>>+	if (!pin)
>>+		return -ENODEV;
>>+	*type = pin->type;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * dpll_pin_signal_type_get - get signal type of a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @type: on success - configured signal type
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got signal type
>>+ * * negative - failed to obtain signal type
>>+ */
>>+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     enum dpll_pin_signal_type *type)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->signal_type_get)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->signal_type_get(ref->dpll, pin, type);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_signal_type_set - set signal type of a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @type: type to be set
>>+ *
>>+ * Return:
>>+ * * 0 - signal type set
>>+ * * negative - failed to set signal type
>>+ */
>>+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     const enum dpll_pin_signal_type type)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>>+		if (!ref->dpll)
>>+			return -EFAULT;
>>+		if (!ref || !ref->ops || !ref->ops->signal_type_set)
>>+			return -EOPNOTSUPP;
>>+		if (ref->dpll != dpll)
>>+			mutex_lock(&ref->dpll->lock);
>>+		ret = ref->ops->signal_type_set(ref->dpll, pin, type);
>>+		if (ref->dpll != dpll)
>>+			mutex_unlock(&ref->dpll->lock);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_signal_type_supported - check if signal type is supported on
>>a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @type: type being checked
>>+ * @supported: on success - if given signal type is supported
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got supported signal type
>>+ * * negative - failed to obtain supported signal type
>>+ */
>>+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>>+				   const struct dpll_pin *pin,
>>+				   const enum dpll_pin_signal_type type,
>>+				   bool *supported)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->signal_type_supported)
>>+		return -EOPNOTSUPP;
>>+	*supported = ref->ops->signal_type_supported(ref->dpll, pin, type);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * dpll_pin_mode_active - check if given mode is active on a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @mode: mode being checked
>>+ * @active: on success - if mode is active
>>+ *
>>+ * Return:
>>+ * * 0 - successfully checked if mode is active
>>+ * * negative - failed to check for active mode
>>+ */
>>+int dpll_pin_mode_active(const struct dpll_device *dpll,
>>+			  const struct dpll_pin *pin,
>>+			  const enum dpll_pin_mode mode,
>>+			  bool *active)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->mode_active)
>>+		return -EOPNOTSUPP;
>>+	*active = ref->ops->mode_active(ref->dpll, pin, mode);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * dpll_pin_mode_supported - check if given mode is supported on a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @mode: mode being checked
>>+ * @supported: on success - if mode is supported
>>+ *
>>+ * Return:
>>+ * * 0 - successfully checked if mode is supported
>>+ * * negative - failed to check for supported mode
>>+ */
>>+int dpll_pin_mode_supported(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     const enum dpll_pin_mode mode,
>>+			     bool *supported)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->mode_supported)
>>+		return -EOPNOTSUPP;
>>+	*supported = ref->ops->mode_supported(ref->dpll, pin, mode);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * dpll_pin_mode_set - set pin's mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @mode: mode being set
>>+ *
>>+ * Return:
>>+ * * 0 - successfully set the mode
>>+ * * negative - failed to set the mode
>>+ */
>>+int dpll_pin_mode_set(const struct dpll_device *dpll,
>>+		       const struct dpll_pin *pin,
>>+		       const enum dpll_pin_mode mode)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>
>I don't understand why you need to call ops->mode_enable for all the
>dplls. The pin is shared, it's the single entity. One call should be
>enough? Why not? Same for other attrs, with exception of PRIO.
>

Single pin can connect multiple dplls. Most probably one call would be enough
if all dplls are controlled by one driver instance. But as example draw from:

https://lore.kernel.org/netdev/DM6PR11MB4657DC9A41A69B71A42DD22F9BFC9@DM6PR11MB4657.namprd11.prod.outlook.com/ 

This is not always the case. So we want to call this on all dplls and let
their drivers decide if there is need to take an action or not.

When registering a pin, kernel module can pass ops=NULL for a dpll that don't
require to be notified with callbacks about pin changes.

>
>>+		if (!ref)
>>+			return -ENODEV;
>>+		if (!ref->ops || !ref->ops->mode_enable)
>>+			return -EOPNOTSUPP;
>>+		if (ref->dpll != dpll)
>>+			mutex_lock(&ref->dpll->lock);
>>+		ret = ref->ops->mode_enable(ref->dpll, pin, mode);
>>+		if (ref->dpll != dpll)
>>+			mutex_unlock(&ref->dpll->lock);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_custom_freq_get - get pin's custom frequency
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @freq: on success - custom frequency of a pin
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got custom frequency
>>+ * * negative - failed to obtain custom frequency
>>+ */
>>+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin, u32 *freq)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>
>How this can happen?
>

Something would be seriously bugged. Think we can remove it.

>
>>+	if (!ref->ops || !ref->ops->custom_freq_get)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->custom_freq_get(ref->dpll, pin, freq);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_custom_freq_set - set pin's custom frequency
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @freq: custom frequency to be set
>>+ *
>>+ * Return:
>>+ * * 0 - successfully set custom frequency
>>+ * * negative - failed to set custom frequency
>>+ */
>>+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin, const u32 freq)
>>+{
>>+	enum dpll_pin_signal_type type;
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>>+		if (!ref)
>>+			return -ENODEV;
>>+		if (!ref->ops || !ref->ops->custom_freq_set ||
>>+		    !ref->ops->signal_type_get)
>>+			return -EOPNOTSUPP;
>>+		if (dpll != ref->dpll)
>>+			mutex_lock(&ref->dpll->lock);
>>+		ret = ref->ops->signal_type_get(dpll, pin, &type);
>>+		if (!ret && type == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ)
>>+			ret = ref->ops->custom_freq_set(ref->dpll, pin, freq);
>>+		if (dpll != ref->dpll)
>>+			mutex_unlock(&ref->dpll->lock);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_prio_get - get pin's prio on dpll
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @prio: on success - priority of a pin on a dpll
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got priority
>>+ * * negative - failed to obtain priority
>>+ */
>>+int dpll_pin_prio_get(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin, u32 *prio)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->prio_get)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->prio_get(ref->dpll, pin, prio);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_prio_set - set pin's prio on dpll
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @prio: priority of a pin to be set on a dpll
>>+ *
>>+ * Return:
>>+ * * 0 - successfully set priority
>>+ * * negative - failed to set the priority
>>+ */
>>+int dpll_pin_prio_set(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin, const u32 prio)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->prio_set)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->prio_set(ref->dpll, pin, prio);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_netifindex_get - get pin's netdev iterface index
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @netifindex: on success - index of a netdevice associated with pin
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got netdev interface index
>>+ * * negative - failed to obtain netdev interface index
>>+ */
>>+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin,
>>+			    int *netifindex)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->net_if_idx_get)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->net_if_idx_get(ref->dpll, pin, netifindex);
>
>return right away.
>

Sure, can do.

>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_description - provide pin's description string
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Return: pointer to a description string.
>>+ */
>>+const char *dpll_pin_description(struct dpll_pin *pin)
>>+{
>>+	return pin->description;
>>+}
>>+
>>+/**
>>+ * dpll_pin_parent - provide pin's parent pin if available
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Return: pointer to aparent if found, NULL otherwise.
>>+ */
>>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)
>
>What exactly is the reason of having one line helpers to access struct
>fields for a struct which is known to the caller? Unneccesary
>boilerplate code. Please remove these. For pin and for dpll_device as
>well.
>

Actually dpll_pin is defined in dpll_core.c, so it is not known to the caller
yet. About dpll_device, yes it is known. And we need common approach here, thus
we need a fix. I know this is kernel code, full of hacks and performance related
bad-design stuff, so will fix as suggested.

>
>
>>+{
>>+	return pin->parent_pin;
>>+}
>>+
>>+/**
>>+ * dpll_mode_set - handler for dpll mode set
>>+ * @dpll: registered dpll pointer
>>+ * @mode: mode to be set
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode)
>>+{
>>+	int ret;
>>+
>>+	if (!dpll->ops || !dpll->ops)
>>+		return -EOPNOTSUPP;
>>+
>>+	ret = dpll->ops->mode_set(dpll, mode);
>
>return right away. You have this pattern on multiple places, please fix.
>

Sure, will fix.

>
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_source_idx_set - handler for selecting a dpll's source
>>+ * @dpll: registered dpll pointer
>>+ * @source_pin_idx: index of a source pin to e selected
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_source_idx_set(struct dpll_device *dpll, const u32
>>source_pin_idx)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	struct dpll_pin *pin;
>>+	int ret;
>>+
>>+	pin = dpll_pin_get_by_idx_from_xa(&dpll->pins, source_pin_idx);
>>+	if (!pin)
>>+		return -ENXIO;
>>+	ref = dpll_pin_find_ref(dpll, pin);
>>+	if (!ref || !ref->ops)
>>+		return -EFAULT;
>>+	if (!ref->ops->select)
>>+		return -ENODEV;
>
>ENODEV definitelly does not look like the correct value here.
>

Sure, will fix.

>
>>+	ret = ref->ops->select(ref->dpll, pin);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_lock - locks the dpll using internal mutex
>>+ * @dpll: registered dpll pointer
>>+ */
>>+void dpll_lock(struct dpll_device *dpll)
>>+{
>>+	mutex_lock(&dpll->lock);
>>+}
>>+
>>+/**
>>+ * dpll_unlock - unlocks the dpll using internal mutex
>>+ * @dpll: registered dpll pointer
>>+ */
>>+void dpll_unlock(struct dpll_device *dpll)
>>+{
>>+	mutex_unlock(&dpll->lock);
>>+}
>>+
>>+enum dpll_pin_type dpll_pin_type(const struct dpll_pin *pin)
>>+{
>>+	return pin->type;
>>+}
>>+
>>+void *dpll_priv(const struct dpll_device *dpll)
>>+{
>>+	return dpll->priv;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_priv);
>>+
>>+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin
>>*pin)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return NULL;
>>+
>>+	return ref->priv;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_priv);
>>+
>>+static int __init dpll_init(void)
>>+{
>>+	int ret;
>>+
>>+	ret = dpll_netlink_init();
>>+	if (ret)
>>+		goto error;
>>+
>>+	ret = class_register(&dpll_class);
>>+	if (ret)
>>+		goto unregister_netlink;
>>+
>>+	return 0;
>>+
>>+unregister_netlink:
>>+	dpll_netlink_finish();
>>+error:
>>+	mutex_destroy(&dpll_device_xa_lock);
>>+	return ret;
>>+}
>>+subsys_initcall(dpll_init);
>>diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
>>new file mode 100644
>>index 000000000000..b933d63b60c1
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.h
>>@@ -0,0 +1,105 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+#ifndef __DPLL_CORE_H__
>>+#define __DPLL_CORE_H__
>>+
>>+#include <linux/dpll.h>
>>+
>>+#include "dpll_netlink.h"
>>+
>>+#define to_dpll_device(_dev) \
>>+	container_of(_dev, struct dpll_device, dev)
>>+
>>+/**
>>+ * struct dpll_device - structure for a DPLL device
>>+ * @id:		unique id number for each device
>>+ * @dev:	struct device for this dpll device
>>+ * @parent:	parent device
>>+ * @ops:	operations this &dpll_device supports
>>+ * @lock:	mutex to serialize operations
>>+ * @type:	type of a dpll
>>+ * @priv:	pointer to private information of owner
>>+ * @pins:	list of pointers to pins registered with this dpll
>>+ * @clock_id:	unique identifier (clock_id) of a dpll
>>+ * @clock_class	quality class of a DPLL clock
>>+ * @dev_driver_idx: provided by driver for
>>+ */
>>+struct dpll_device {
>>+	u32 id;
>>+	struct device dev;
>>+	struct device *parent;
>>+	struct dpll_device_ops *ops;
>>+	struct mutex lock;
>>+	enum dpll_type type;
>>+	void *priv;
>>+	struct xarray pins;
>>+	u64 clock_id;
>>+	enum dpll_clock_class clock_class;
>>+	u8 dev_driver_idx;
>>+};
>>+
>>+#define for_each_pin_on_dpll(dpll, pin, i)			\
>>+	for (pin = dpll_pin_first(dpll, &i); pin != NULL;	\
>>+	     pin = dpll_pin_next(dpll, &i))
>
>What is the purpose for this macro and dpll_pin_first/next() helper?
>Why is this not equivalent to:
>xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED)
>

At some point of implementation, struct dpll_device was private for dpll_core.c
now we can probably have it simplified, will fix it.

>
>>+
>>+#define for_each_dpll(dpll, i)                         \
>>+	for (dpll = dpll_first(&i); dpll != NULL; dpll = dpll_next(&i))
>
>Same here, why this macro and helpers are needed?
>

The XA of DPLLs is private to dpll_core.c

>
>>+
>>+struct dpll_device *dpll_device_get_by_id(int id);
>>+
>>+struct dpll_device *dpll_device_get_by_name(const char *name);
>>+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long
>*index);
>>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long
>*index);
>>+struct dpll_device *dpll_first(unsigned long *index);
>>+struct dpll_device *dpll_next(unsigned long *index);
>>+void dpll_device_unregister(struct dpll_device *dpll);
>>+u32 dpll_id(struct dpll_device *dpll);
>>+const char *dpll_dev_name(struct dpll_device *dpll);
>>+void dpll_lock(struct dpll_device *dpll);
>>+void dpll_unlock(struct dpll_device *dpll);
>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>>+int dpll_pin_type_get(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin,
>>+		      enum dpll_pin_type *type);
>>+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     enum dpll_pin_signal_type *type);
>>+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     const enum dpll_pin_signal_type type);
>>+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>>+				   const struct dpll_pin *pin,
>>+				   const enum dpll_pin_signal_type type,
>>+				   bool *supported);
>>+int dpll_pin_mode_active(const struct dpll_device *dpll,
>>+			 const struct dpll_pin *pin,
>>+			 const enum dpll_pin_mode mode,
>>+			 bool *active);
>>+int dpll_pin_mode_supported(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin,
>>+			    const enum dpll_pin_mode mode,
>>+			    bool *supported);
>>+int dpll_pin_mode_set(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin,
>>+		      const enum dpll_pin_mode mode);
>>+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin, u32 *freq);
>>+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin, const u32 freq);
>>+int dpll_pin_prio_get(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin, u32 *prio);
>>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx);
>>+int dpll_pin_prio_set(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin, const u32 prio);
>>+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin,
>>+			    int *netifindex);
>>+const char *dpll_pin_description(struct dpll_pin *pin);
>>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin);
>>+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode);
>>+int dpll_source_idx_set(struct dpll_device *dpll, const u32
>>source_pin_idx);
>>+
>>+#endif
>>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>>new file mode 100644
>>index 000000000000..91a1e5025ab2
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.c
>>@@ -0,0 +1,883 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ * Generic netlink for DPLL management framework
>>+ *
>>+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ *
>>+ */
>>+#include <linux/module.h>
>>+#include <linux/kernel.h>
>>+#include <net/genetlink.h>
>>+#include "dpll_core.h"
>>+
>>+#include <uapi/linux/dpll.h>
>>+
>>+static const struct genl_multicast_group dpll_mcgrps[] = {
>>+	{ .name = DPLL_MONITOR_GROUP_NAME,  },
>>+};
>>+
>>+static const struct nla_policy dpll_cmd_device_get_policy[] = {
>>+	[DPLLA_ID]		= { .type = NLA_U32 },
>>+	[DPLLA_NAME]		= { .type = NLA_STRING,
>>+				    .len = DPLL_NAME_LEN },
>>+	[DPLLA_FILTER]		= { .type = NLA_U32 },
>>+};
>>+
>>+static const struct nla_policy dpll_cmd_device_set_policy[] = {
>>+	[DPLLA_ID]		= { .type = NLA_U32 },
>>+	[DPLLA_NAME]		= { .type = NLA_STRING,
>>+				    .len = DPLL_NAME_LEN },
>>+	[DPLLA_MODE]		= { .type = NLA_U32 },
>>+	[DPLLA_SOURCE_PIN_IDX]	= { .type = NLA_U32 },
>>+};
>>+
>>+static const struct nla_policy dpll_cmd_pin_set_policy[] = {
>>+	[DPLLA_ID]		= { .type = NLA_U32 },
>>+	[DPLLA_PIN_IDX]		= { .type = NLA_U32 },
>>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>>+	[DPLLA_PIN_MODE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>>+};
>>+
>>+struct dpll_dump_ctx {
>>+	int dump_filter;
>>+};
>>+
>>+static struct genl_family dpll_gnl_family;
>>+
>>+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback
>*cb)
>>+{
>>+	return (struct dpll_dump_ctx *)cb->ctx;
>>+}
>>+
>>+static int dpll_msg_add_id(struct sk_buff *msg, u32 id)
>>+{
>>+	if (nla_put_u32(msg, DPLLA_ID, id))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_name(struct sk_buff *msg, const char *name)
>>+{
>>+	if (nla_put_string(msg, DPLLA_NAME, name))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int __dpll_msg_add_mode(struct sk_buff *msg, enum dplla msg_type,
>>+			       enum dpll_mode mode)
>>+{
>>+	if (nla_put_s32(msg, msg_type, mode))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll)
>>+{
>>+	enum dpll_mode m;
>>+	int ret;
>>+
>>+	if (!dpll->ops->mode_get)
>>+		return 0;
>>+	ret = dpll->ops->mode_get(dpll, &m);
>>+	if (ret)
>>+		return ret;
>>+
>>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>>+}
>>+
>>+static int
>>+dpll_msg_add_modes_supported(struct sk_buff *msg,
>>+			     const struct dpll_device *dpll)
>>+{
>>+	enum dpll_mode i;
>>+	int ret = 0;
>>+
>>+	if (!dpll->ops->mode_supported)
>>+		return ret;
>>+
>>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>>+		if (dpll->ops->mode_supported(dpll, i)) {
>>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_msg_add_clock_id(struct sk_buff *msg,
>>+				 const struct dpll_device *dpll)
>>+{
>>+	if (nla_put_64bit(msg, DPLLA_CLOCK_ID, sizeof(dpll->clock_id),
>>+			  &dpll->clock_id, 0))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_clock_class(struct sk_buff *msg,
>>+				    const struct dpll_device *dpll)
>>+{
>>+	if (nla_put_s32(msg, DPLLA_CLOCK_CLASS, dpll->clock_class))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_device *dpll)
>>+{
>>+	u32 source_idx;
>>+	int ret;
>>+
>>+	if (!dpll->ops->source_pin_idx_get)
>>+		return 0;
>>+	ret = dpll->ops->source_pin_idx_get(dpll, &source_idx);
>>+	if (ret)
>>+		return ret;
>>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct
>>dpll_device *dpll)
>>+{
>>+	enum dpll_lock_status s;
>>+	int ret;
>>+
>>+	if (!dpll->ops->lock_status_get)
>>+		return 0;
>>+	ret = dpll->ops->lock_status_get(dpll, &s);
>>+	if (ret)
>>+		return ret;
>>+	if (nla_put_s32(msg, DPLLA_LOCK_STATUS, s))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device
>*dpll)
>>+{
>>+	s32 temp;
>>+	int ret;
>>+
>>+	if (!dpll->ops->temp_get)
>>+		return 0;
>>+	ret = dpll->ops->temp_get(dpll, &temp);
>>+	if (ret)
>>+		return ret;
>>+	if (nla_put_u32(msg, DPLLA_TEMP, temp))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_idx(struct sk_buff *msg, u32 pin_idx)
>>+{
>>+	if (nla_put_u32(msg, DPLLA_PIN_IDX, pin_idx))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_description(struct sk_buff *msg,
>>+					const char *description)
>>+{
>>+	if (nla_put_string(msg, DPLLA_PIN_DESCRIPTION, description))
>
>I don't understand the reason to have these helpers. I said that before,
>just call nla_put_* directly and avoid these unnecessary boilerplate
>unctions.
>

Sure, previously there were some parts reused, rest was for consistency,
I think we can remove all of them in next version.

>
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_parent_idx(struct sk_buff *msg, u32
>>parent_idx)
>>+{
>>+	if (nla_put_u32(msg, DPLLA_PIN_PARENT_IDX, parent_idx))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_device
>>*dpll,
>>+		      const struct dpll_pin *pin)
>>+{
>>+	enum dpll_pin_type t;
>>+
>>+	if (dpll_pin_type_get(dpll, pin, &t))
>>+		return 0;
>>+
>>+	if (nla_put_s32(msg, DPLLA_PIN_TYPE, t))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int __dpll_msg_add_pin_signal_type(struct sk_buff *msg,
>>+					  enum dplla attr,
>>+					  enum dpll_pin_signal_type type)
>>+{
>>+	if (nla_put_s32(msg, attr, type))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_signal_type(struct sk_buff *msg,
>>+					const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin)
>>+{
>>+	enum dpll_pin_signal_type t;
>
>s/t/type/
>

Sure, gonna fix.

>
>>+	int ret;
>>+
>>+	if (dpll_pin_signal_type_get(dpll, pin, &t))
>
>Why don't you propagate the error value?
>

The driver which have not implemented the callback might do it on purpose,
I will fix it by narrowing return 0 here only for missing callback.

>
>>+		return 0;
>>+	ret = __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>>+	if (ret)
>>+		return ret;
>>+
>>+	if (t == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) {
>>+		u32 freq;
>>+
>>+		if (dpll_pin_custom_freq_get(dpll, pin, &freq))
>>+			return 0;
>>+		if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>>+			return -EMSGSIZE;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>>+					const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin)
>>+{
>>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>>+	enum dpll_pin_signal_type i;
>>+	bool supported;
>>+
>>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>>+		if (dpll_pin_signal_type_supported(dpll, pin, i, &supported))
>>+			continue;
>>+		if (supported) {
>>+			int ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>>+
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>+				   const struct dpll_device *dpll,
>>+				   const struct dpll_pin *pin)
>>+{
>>+	enum dpll_pin_mode i;
>>+	bool active;
>>+
>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>+			return 0;
>>+		if (active)
>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>
>Why this is signed?
>

Because enums are signed.

>
>>+				return -EMSGSIZE;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_modes_supported(struct sk_buff *msg,
>>+					     const struct dpll_device *dpll,
>>+					     const struct dpll_pin *pin)
>>+{
>>+	enum dpll_pin_mode i;
>>+	bool supported;
>>+
>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>+		if (dpll_pin_mode_supported(dpll, pin, i, &supported))
>>+			return 0;
>>+		if (supported)
>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE_SUPPORTED, i))
>
>Here too. Please check the rest, you should not need to put signed
>values.
>

Enums are signed, don't understand why you want to mix types?

>
>>+				return -EMSGSIZE;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_device
>*dpll,
>>+		      const struct dpll_pin *pin)
>>+{
>>+	u32 prio;
>>+
>>+	if (dpll_pin_prio_get(dpll, pin, &prio))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_PIN_PRIO, prio))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct dpll_device
>>*dpll,
>>+			    const struct dpll_pin *pin)
>>+{
>>+	int netifindex;
>>+
>>+	if (dpll_pin_netifindex_get(dpll, pin, &netifindex))
>>+		return 0;
>>+	if (nla_put_s32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+__dpll_cmd_device_dump_one(struct sk_buff *msg, struct dpll_device *dpll)
>>+{
>>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
>>+
>>+	return ret;
>>+}
>>+
>>+static int
>>+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_device *dpll,
>>+			struct dpll_pin *pin)
>>+{
>>+	struct dpll_pin *parent = NULL;
>>+	int ret;
>>+
>>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_description(msg, dpll_pin_description(pin));
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_type(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	parent = dpll_pin_parent(pin);
>>+	if (parent) {
>>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>>+								    parent));
>>+		if (ret)
>>+			return ret;
>>+	}
>>+	ret = dpll_msg_add_pin_signal_type(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_signal_types_supported(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_modes(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_modes_supported(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_prio(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_netifindex(msg, dpll, pin);
>>+
>>+	return ret;
>>+}
>>+
>>+static int __dpll_cmd_dump_pins(struct sk_buff *msg, struct dpll_device
>>*dpll)
>>+{
>>+	struct dpll_pin *pin;
>>+	struct nlattr *attr;
>>+	unsigned long i;
>>+	int ret = 0;
>>+
>>+	for_each_pin_on_dpll(dpll, pin, i) {
>>+		attr = nla_nest_start(msg, DPLLA_PIN);
>>+		if (!attr) {
>>+			ret = -EMSGSIZE;
>>+			goto nest_cancel;
>>+		}
>>+		ret = __dpll_cmd_pin_dump_one(msg, dpll, pin);
>>+		if (ret)
>>+			goto nest_cancel;
>>+		nla_nest_end(msg, attr);
>>+	}
>>+
>>+	return ret;
>>+
>>+nest_cancel:
>>+	nla_nest_cancel(msg, attr);
>>+	return ret;
>>+}
>>+
>>+static int
>>+__dpll_cmd_dump_status(struct sk_buff *msg, struct dpll_device *dpll)
>>+{
>>+	int ret = dpll_msg_add_source_pin(msg, dpll);
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_temp(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_lock_status(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_mode(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_modes_supported(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_clock_id(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_clock_class(msg, dpll);
>>+
>>+	return ret;
>>+}
>>+
>>+static int
>>+dpll_device_dump_one(struct dpll_device *dpll, struct sk_buff *msg,
>>+		     int dump_filter)
>>+{
>>+	int ret;
>>+
>>+	dpll_lock(dpll);
>>+	ret = __dpll_cmd_device_dump_one(msg, dpll);
>>+	if (ret)
>>+		goto out_unlock;
>>+
>>+	if (dump_filter & DPLL_FILTER_STATUS) {
>>+		ret = __dpll_cmd_dump_status(msg, dpll);
>>+		if (ret) {
>>+			if (ret != -EMSGSIZE)
>>+				ret = -EAGAIN;
>>+			goto out_unlock;
>>+		}
>>+	}
>>+	if (dump_filter & DPLL_FILTER_PINS)
>>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>>+	dpll_unlock(dpll);
>>+
>>+	return ret;
>>+out_unlock:
>>+	dpll_unlock(dpll);
>>+	return ret;
>>+}
>>+
>>+static int
>>+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
>>+			 struct dpll_pin *pin, struct genl_info *info)
>>+{
>>+	enum dpll_pin_signal_type st;
>>+	enum dpll_pin_mode mode;
>>+	struct nlattr *a;
>>+	int rem, ret = 0;
>>+	u32 prio, freq;
>>+
>>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>+			  genlmsg_len(info->genlhdr), rem) {
>>+		switch (nla_type(a)) {
>>+		case DPLLA_PIN_SIGNAL_TYPE:
>>+			st = nla_get_s32(a);
>>+			ret = dpll_pin_signal_type_set(dpll, pin, st);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_CUSTOM_FREQ:
>>+			freq = nla_get_u32(a);
>>+			ret = dpll_pin_custom_freq_set(dpll, pin, freq);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_MODE:
>>+			mode = nla_get_s32(a);
>>+			ret = dpll_pin_mode_set(dpll, pin, mode);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_PRIO:
>>+			prio = nla_get_u32(a);
>>+			ret = dpll_pin_prio_set(dpll, pin, prio);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		default:
>>+			break;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_cmd_pin_set(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	struct nlattr **attrs = info->attrs;
>>+	struct dpll_pin *pin;
>>+	int pin_id;
>>+
>>+	if (!attrs[DPLLA_PIN_IDX])
>>+		return -EINVAL;
>>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>>+	dpll_lock(dpll);
>>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>>+	dpll_unlock(dpll);
>>+	if (!pin)
>>+		return -ENODEV;
>>+	return dpll_pin_set_from_nlattr(dpll, pin, info);
>>+}
>>+
>>+enum dpll_mode dpll_msg_read_mode(struct nlattr *a)
>>+{
>>+	return nla_get_s32(a);
>>+}
>>+
>>+u32 dpll_msg_read_source_pin_id(struct nlattr *a)
>>+{
>>+	return nla_get_u32(a);
>>+}
>>+
>>+static int
>>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>>+{
>>+	enum dpll_mode m;
>>+	struct nlattr *a;
>>+	int rem, ret = 0;
>>+	u32 source_pin;
>>+
>>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>+			  genlmsg_len(info->genlhdr), rem) {
>>+		switch (nla_type(a)) {
>>+		case DPLLA_MODE:
>>+			m = dpll_msg_read_mode(a);
>>+
>>+			ret = dpll_mode_set(dpll, m);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_SOURCE_PIN_IDX:
>>+			source_pin = dpll_msg_read_source_pin_id(a);
>>+
>>+			ret = dpll_source_idx_set(dpll, source_pin);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		default:
>>+			break;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_cmd_device_set(struct sk_buff *skb, struct genl_info
>>*info)
>>+{
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+
>>+	return dpll_set_from_nlattr(dpll, info);
>>+}
>>+
>>+static int
>>+dpll_cmd_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
>>+{
>>+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>>+	struct dpll_device *dpll;
>>+	struct nlattr *hdr;
>>+	unsigned long i;
>>+	int ret;
>>+
>>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh-
>>nlmsg_seq,
>>+			  &dpll_gnl_family, 0, DPLL_CMD_DEVICE_GET);
>>+	if (!hdr)
>>+		return -EMSGSIZE;
>>+
>>+	for_each_dpll(dpll, i) {
>>+		ret = dpll_device_dump_one(dpll, skb, ctx->dump_filter);
>>+		if (ret)
>>+			break;
>>+	}
>>+
>>+	if (ret)
>>+		genlmsg_cancel(skb, hdr);
>>+	else
>>+		genlmsg_end(skb, hdr);
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_cmd_device_get(struct sk_buff *skb, struct genl_info
>>*info)
>>+{
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	struct nlattr **attrs = info->attrs;
>>+	struct sk_buff *msg;
>>+	int dump_filter = 0;
>>+	struct nlattr *hdr;
>>+	int ret;
>>+
>>+	if (attrs[DPLLA_FILTER])
>>+		dump_filter = nla_get_s32(attrs[DPLLA_FILTER]);
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+	hdr = genlmsg_put_reply(msg, info, &dpll_gnl_family, 0,
>>+				DPLL_CMD_DEVICE_GET);
>>+	if (!hdr)
>>+		return -EMSGSIZE;
>>+
>>+	ret = dpll_device_dump_one(dpll, msg, dump_filter);
>>+	if (ret)
>>+		goto out_free_msg;
>>+	genlmsg_end(msg, hdr);
>>+
>>+	return genlmsg_reply(msg, info);
>>+
>>+out_free_msg:
>>+	nlmsg_free(msg);
>>+	return ret;
>>+
>>+}
>>+
>>+static int dpll_cmd_device_get_start(struct netlink_callback *cb)
>>+{
>>+	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
>>+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>>+	struct nlattr *attr = info->attrs[DPLLA_FILTER];
>>+
>>+	if (attr)
>>+		ctx->dump_filter = nla_get_s32(attr);
>>+	else
>>+		ctx->dump_filter = 0;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff
>*skb,
>>+			 struct genl_info *info)
>>+{
>>+	struct dpll_device *dpll_id = NULL, *dpll_name = NULL;
>>+
>>+	if (!info->attrs[DPLLA_ID] &&
>>+	    !info->attrs[DPLLA_NAME])
>>+		return -EINVAL;
>>+
>>+	if (info->attrs[DPLLA_ID]) {
>>+		u32 id = nla_get_u32(info->attrs[DPLLA_ID]);
>>+
>>+		dpll_id = dpll_device_get_by_id(id);
>>+		if (!dpll_id)
>>+			return -ENODEV;
>>+		info->user_ptr[0] = dpll_id;
>>+	}
>>+	if (info->attrs[DPLLA_NAME]) {
>>+		const char *name = nla_data(info->attrs[DPLLA_NAME]);
>>+
>>+		dpll_name = dpll_device_get_by_name(name);
>>+		if (!dpll_name)
>>+			return -ENODEV;
>>+
>>+		if (dpll_id && dpll_name != dpll_id)
>>+			return -EINVAL;
>>+		info->user_ptr[0] = dpll_name;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static const struct genl_ops dpll_ops[] = {
>>+	{
>>+		.cmd	= DPLL_CMD_DEVICE_GET,
>>+		.flags  = GENL_UNS_ADMIN_PERM,
>>+		.start	= dpll_cmd_device_get_start,
>>+		.dumpit	= dpll_cmd_device_dump,
>>+		.doit	= dpll_cmd_device_get,
>>+		.policy	= dpll_cmd_device_get_policy,
>>+		.maxattr = ARRAY_SIZE(dpll_cmd_device_get_policy) - 1,
>>+	},
>>+	{
>>+		.cmd	= DPLL_CMD_DEVICE_SET,
>>+		.flags	= GENL_UNS_ADMIN_PERM,
>>+		.doit	= dpll_cmd_device_set,
>>+		.policy	= dpll_cmd_device_set_policy,
>>+		.maxattr = ARRAY_SIZE(dpll_cmd_device_set_policy) - 1,
>>+	},
>>+	{
>>+		.cmd	= DPLL_CMD_PIN_SET,
>>+		.flags	= GENL_UNS_ADMIN_PERM,
>>+		.doit	= dpll_cmd_pin_set,
>>+		.policy	= dpll_cmd_pin_set_policy,
>>+		.maxattr = ARRAY_SIZE(dpll_cmd_pin_set_policy) - 1,
>>+	},
>>+};
>>+
>>+static struct genl_family dpll_family __ro_after_init = {
>>+	.hdrsize	= 0,
>
>No need for static.
>

Sorry, don't get it, why it shall be non-static?

>
>>+	.name		= DPLL_FAMILY_NAME,
>>+	.version	= DPLL_VERSION,
>>+	.ops		= dpll_ops,
>>+	.n_ops		= ARRAY_SIZE(dpll_ops),
>>+	.mcgrps		= dpll_mcgrps,
>>+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
>>+	.pre_doit	= dpll_pre_doit,
>>+	.parallel_ops   = true,
>>+};
>>+
>>+static int dpll_event_device_id(struct sk_buff *msg, struct dpll_device
>>*dpll)
>>+{
>>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_event_device_change(struct sk_buff *msg,
>>+				    struct dpll_device *dpll,
>>+				    struct dpll_pin *pin,
>>+				    enum dpll_event_change event)
>>+{
>>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = nla_put_s32(msg, DPLLA_CHANGE_TYPE, event);
>>+	if (ret)
>>+		return ret;
>>+	switch (event)	{
>>+	case DPLL_CHANGE_PIN_ADD:
>>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>>+	case DPLL_CHANGE_PIN_MODE:
>>+	case DPLL_CHANGE_PIN_PRIO:
>>+		ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>>+		break;
>>+	default:
>>+		break;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/*
>>+ * Generic netlink DPLL event encoding
>>+ */
>>+static int dpll_send_event_create(enum dpll_event event,
>>+				  struct dpll_device *dpll)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+
>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>>+	if (!hdr)
>>+		goto out_free_msg;
>>+
>>+	ret = dpll_event_device_id(msg, dpll);
>>+	if (ret)
>>+		goto out_cancel_msg;
>>+	genlmsg_end(msg, hdr);
>>+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
>>+
>>+	return 0;
>>+
>>+out_cancel_msg:
>>+	genlmsg_cancel(msg, hdr);
>>+out_free_msg:
>>+	nlmsg_free(msg);
>>+
>>+	return ret;
>>+}
>>+
>>+/*
>>+ * Generic netlink DPLL event encoding
>>+ */
>>+static int dpll_send_event_change(struct dpll_device *dpll,
>>+				  struct dpll_pin *pin,
>>+				  enum dpll_event_change event)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+
>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0,
>>DPLL_EVENT_DEVICE_CHANGE);
>>+	if (!hdr)
>>+		goto out_free_msg;
>>+
>>+	ret = dpll_event_device_change(msg, dpll, pin, event);
>>+	if (ret)
>>+		goto out_cancel_msg;
>>+	genlmsg_end(msg, hdr);
>>+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
>>+
>>+	return 0;
>>+
>>+out_cancel_msg:
>>+	genlmsg_cancel(msg, hdr);
>>+out_free_msg:
>>+	nlmsg_free(msg);
>>+
>>+	return ret;
>>+}
>>+
>>+int dpll_notify_device_create(struct dpll_device *dpll)
>>+{
>>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
>>+}
>>+
>>+int dpll_notify_device_delete(struct dpll_device *dpll)
>>+{
>>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
>>+}
>>+
>>+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change
>>event)
>>+{
>>+	return dpll_send_event_change(dpll, NULL, event);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_notify);
>>+
>>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		    enum dpll_event_change event)
>>+{
>>+	return dpll_send_event_change(dpll, pin, event);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_notify);
>>+
>>+int __init dpll_netlink_init(void)
>>+{
>>+	return genl_register_family(&dpll_family);
>>+}
>>+
>>+void dpll_netlink_finish(void)
>>+{
>>+	genl_unregister_family(&dpll_family);
>>+}
>>+
>>+void __exit dpll_netlink_fini(void)
>>+{
>>+	dpll_netlink_finish();
>>+}
>>diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
>>new file mode 100644
>>index 000000000000..8e50b2493027
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.h
>>@@ -0,0 +1,24 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+/**
>>+ * dpll_notify_device_create - notify that the device has been created
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_notify_device_create(struct dpll_device *dpll);
>>+
>>+
>>+/**
>>+ * dpll_notify_device_delete - notify that the device has been deleted
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_notify_device_delete(struct dpll_device *dpll);
>>+
>>+int __init dpll_netlink_init(void);
>>+void dpll_netlink_finish(void);
>>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>>new file mode 100644
>>index 000000000000..fcba46ea1b7b
>>--- /dev/null
>>+++ b/include/linux/dpll.h
>>@@ -0,0 +1,282 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+#ifndef __DPLL_H__
>>+#define __DPLL_H__
>>+
>>+#include <uapi/linux/dpll.h>
>>+#include <linux/device.h>
>>+
>>+struct dpll_device;
>>+struct dpll_pin;
>>+
>>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>>+
>>+struct dpll_device_ops {
>>+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode
>>*mode);
>>+	int (*mode_set)(const struct dpll_device *dpll,
>>+			const enum dpll_mode mode);
>>+	bool (*mode_supported)(const struct dpll_device *dpll,
>>+			       const enum dpll_mode mode);
>>+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
>>+				  u32 *pin_idx);
>>+	int (*lock_status_get)(const struct dpll_device *dpll,
>>+			       enum dpll_lock_status *status);
>>+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp);
>>+};
>>+
>>+struct dpll_pin_ops {
>>+	int (*signal_type_get)(const struct dpll_device *dpll,
>>+			       const struct dpll_pin *pin,
>>+			       enum dpll_pin_signal_type *type);
>>+	int (*signal_type_set)(const struct dpll_device *dpll,
>>+			       const struct dpll_pin *pin,
>>+			       const enum dpll_pin_signal_type type);
>>+	bool (*signal_type_supported)(const struct dpll_device *dpll,
>>+				      const struct dpll_pin *pin,
>>+				      const enum dpll_pin_signal_type type);
>>+	int (*custom_freq_set)(const struct dpll_device *dpll,
>>+			       const struct dpll_pin *pin,
>>+			       const u32 custom_freq);
>>+	int (*custom_freq_get)(const struct dpll_device *dpll,
>>+			       const struct dpll_pin *pin,
>>+			       u32 *custom_freq);
>>+	bool (*mode_active)(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     const enum dpll_pin_mode mode);
>>+	int (*mode_enable)(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin,
>>+			    const enum dpll_pin_mode mode);
>>+	bool (*mode_supported)(const struct dpll_device *dpll,
>>+				const struct dpll_pin *pin,
>>+				const enum dpll_pin_mode mode);
>>+	int (*prio_get)(const struct dpll_device *dpll,
>>+			const struct dpll_pin *pin,
>>+			u32 *prio);
>>+	int (*prio_set)(const struct dpll_device *dpll,
>>+			const struct dpll_pin *pin,
>>+			const u32 prio);
>>+	int (*net_if_idx_get)(const struct dpll_device *dpll,
>>+			      const struct dpll_pin *pin,
>>+			      int *net_if_idx);
>>+	int (*select)(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin);
>
>Could you please pass extack to all of the ops? I think it is important
>to give the user the meaningfull error message from the start.
>

Sure, will add that.

>
>>+};
>>+
>>+/**
>>+ * dpll_device_alloc - allocate memory for a new dpll_device object
>>+ * @ops: pointer to dpll operations structure
>>+ * @type: type of a dpll being allocated
>>+ * @clock_id: a system unique number for a device
>>+ * @clock_class: quality class of a DPLL clock
>>+ * @dev_driver_idx: index of dpll device on parent device
>>+ * @priv: private data of a registerer
>>+ * @parent: device structure of a module registering dpll device
>>+ *
>>+ * Allocate memory for a new dpll and initialize it with its type, name,
>>+ * callbacks and private data pointer.
>>+ *
>>+ * Name is generated based on: parent driver, type and dev_driver_idx.
>>+ * Finding allocated and registered dpll device is also possible with
>>+ * the: clock_id, type and dev_driver_idx. This way dpll device can be
>>+ * shared by multiple instances of a device driver.
>>+ *
>>+ * Returns:
>>+ * * pointer to initialized dpll - success
>>+ * * NULL - memory allocation fail
>>+ */
>>+struct dpll_device
>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>>+		   u8 dev_driver_idx, void *priv, struct device *parent);
>>+
>>+/**
>>+ * dpll_device_unregister - unregister registered dpll
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Unregister the dpll from the subsystem, make it unavailable for
>>netlink
>>+ * API users.
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_device_free - free dpll memory
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Free memory allocated with ``dpll_device_alloc(..)``
>>+ */
>>+void dpll_device_free(struct dpll_device *dpll);
>
>
>Could you please sort the functions? I mean, dpll_device_unregister() in
>currently in the middle of dpll_device_alloc() and dpll_device_free()
>
>Also, there is no dpll_device_register(), that is odd.
>

Sure, will fix it.

>
>>+
>>+/**
>>+ * dpll_priv - get private data
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Obtain private data pointer passed to dpll subsystem when allocating
>>+ * device with ``dpll_device_alloc(..)``
>>+ */
>>+void *dpll_priv(const struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_pin_priv - get private data
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Obtain private pin data pointer passed to dpll subsystem when pin
>>+ * was registered with dpll.
>>+ */
>>+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin
>>*pin);
>>+
>>+/**
>>+ * dpll_pin_idx - get pin idx
>>+ * @dpll: pointer to dpll
>>+ * @pin: pointer to a pin
>>+ *
>>+ * Obtain pin index of given pin on given dpll.
>>+ *
>>+ * Return: PIN_IDX_INVALID - if failed to find pin, otherwise pin index
>>+ */
>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>
>You don't use this in driver (and I don't see any need for that). Remove
>from the public header.
>

Sure, will do.

>
>>+
>>+/**
>>+ * dpll_shared_pin_register - share a pin between dpll devices
>>+ * @dpll_pin_owner: a dpll already registered with a pin
>>+ * @dpll: a dpll being registered with a pin
>>+ * @shared_pin_description: identifies pin registered with dpll device
>>+ *	(@dpll_pin_owner) which is now being registered with new dpll (@dpll)
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ *
>>+ * Register a pin already registered with different dpll device.
>>+ * Allow to share a single pin within multiple dpll instances.
>>+ *
>>+ * Returns:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+int
>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>+			 struct dpll_device *dpll,
>>+			 const char *shared_pin_description,
>>+			 struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_pin_alloc - allocate memory for a new dpll_pin object
>>+ * @description: pointer to string description of a pin with max length
>>+ * equal to PIN_DESC_LEN
>>+ * @type: type of allocated pin
>>+ *
>>+ * Allocate memory for a new pin and initialize its resources.
>>+ *
>>+ * Returns:
>>+ * * pointer to initialized pin - success
>>+ * * NULL - memory allocation fail
>>+ */
>>+struct dpll_pin *dpll_pin_alloc(const char *description,
>>+				const enum dpll_pin_type type);
>>+
>>+/**
>>+ * dpll_pin_register - register pin with a dpll device
>>+ * @dpll: pointer to dpll object to register pin with
>>+ * @pin: pointer to allocated pin object being registered with dpll
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ *
>>+ * Register previously allocated pin object with a dpll device.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this dpll,
>>+ * * -EBUSY - couldn't allocate id for a pin.
>>+ */
>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_pin_deregister - deregister pin from a dpll device
>>+ * @dpll: pointer to dpll object to deregister pin from
>>+ * @pin: pointer to allocated pin object being deregistered from dpll
>>+ *
>>+ * Deregister previously registered pin object from a dpll device.
>>+ *
>>+ * Return:
>>+ * * 0 - pin was successfully deregistered from this dpll device,
>>+ * * -ENXIO - given pin was not registered with this dpll device,
>>+ * * -EINVAL - pin pointer is not valid.
>>+ */
>>+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_pin_free - free memory allocated for a pin
>>+ * @pin: pointer to allocated pin object being freed
>>+ *
>>+ * Shared pins must be deregistered from all dpll devices before freeing
>>them,
>>+ * otherwise the memory won't be freed.
>>+ */
>>+void dpll_pin_free(struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_muxed_pin_register - register a pin to a muxed-type pin
>>+ * @parent_pin_description: parent pin description as given on it's
>allocation
>>+ * @pin: pointer to allocated pin object being registered with a parent
>>pin
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops*
>>+ *
>>+ * In case of multiplexed pins, allows registring them under a single
>>+ * parent pin.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this parent pin,
>>+ * * -EBUSY - couldn't assign id for a pin.
>>+ */
>>+int dpll_muxed_pin_register(struct dpll_device *dpll,
>>+			    const char *parent_pin_description,
>>+			    struct dpll_pin *pin,
>>+			    struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_device_get_by_clock_id - find a dpll by its clock_id, type and
>>index
>>+ * @clock_id: clock_id of dpll, as given by driver on
>>``dpll_device_alloc``
>>+ * @type: type of dpll, as given by driver on ``dpll_device_alloc``
>>+ * @idx: index of dpll, as given by driver on ``dpll_device_alloc``
>>+ *
>>+ * Allows multiple driver instances using one physical DPLL to find
>>+ * and share already registered DPLL device.
>>+ *
>>+ * Return: pointer if device was found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>>+						enum dpll_type type, u8 idx);
>>+
>>+/**
>>+ * dpll_device_notify - notify on dpll device change
>>+ * @dpll: dpll device pointer
>>+ * @event: type of change
>>+ *
>>+ * Broadcast event to the netlink multicast registered listeners.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change
>>event);
>>+
>>+/**
>>+ * dpll_pin_notify - notify on dpll pin change
>>+ * @dpll: dpll device pointer
>>+ * @pin: dpll pin pointer
>>+ * @event: type of change
>>+ *
>>+ * Broadcast event to the netlink multicast registered listeners.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		    enum dpll_event_change event);
>
>You don't use this from driver, remove it from the public header.
>

Sure, makes sense.

>
>>+
>>+#endif
>>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>>new file mode 100644
>>index 000000000000..b7dbdd814b5c
>>--- /dev/null
>>+++ b/include/uapi/linux/dpll.h
>>@@ -0,0 +1,294 @@
>>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>+#ifndef _UAPI_LINUX_DPLL_H
>>+#define _UAPI_LINUX_DPLL_H
>>+
>>+#define DPLL_NAME_LEN		32
>>+#define DPLL_DESC_LEN		20
>>+#define DPLL_PIN_DESC_LEN	20
>
>I don't see why to limit this. Those strings are read only. See
>DEVLINK_ATTR_BUS_NAME for example.
>

Sure, I think Vadim wanted to take care of it.

>
>>+
>>+/* Adding event notification support elements */
>>+#define DPLL_FAMILY_NAME	"dpll"
>>+#define DPLL_VERSION		0x01
>>+#define DPLL_MONITOR_GROUP_NAME	"monitor"
>>+
>>+#define DPLL_FILTER_PINS	1
>>+#define DPLL_FILTER_STATUS	2
>
>Why again do we need any filtering here?
>

A way to reduce output generated by dump/get requests. Assume the userspace
want to have specific information instead of everything in one packet.
They might be not needed after we introduce separated "get pin" command.

>
>>+
>>+/* dplla - Attributes of dpll generic netlink family
>>+ *
>>+ * @DPLLA_UNSPEC - invalid attribute
>>+ * @DPLLA_ID - ID of a dpll device (unsigned int)
>>+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH
>size)
>>+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
>>+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum
>dpll_mode)
>>+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll
>
>IDX
>

Sure, will fix.

>
>>+ *	(unsigned int)
>>+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
>>+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)
>
>Hmm, wouldn't it be better to have it as 1/10 of Celsius degree for
>example?
>

As we are not using it, I don't have any strong opinon on this, but seems
resonable to me, will add a divider into uAPI like:

#define DPLL_TEMP_DIVIDER	10

>
>>+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
>>+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum
>dpll_clock_class)
>>+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests
>>(int,
>>+ *	sum of DPLL_DUMP_FILTER_* defines)
>>+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
>>+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
>>+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by
>>driver
>>+ *	(char array of PIN_DESC_LEN size)
>>+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
>>+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
>>+ *	(enum dpll_pin_signal_type)
>>+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
>>+ *	(enum dpll_pin_signal_type)
>>+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for
>>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
>>+ *	(unsigned int)
>>+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
>>+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
>>+ *	(enum dpll_pin_mode)
>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>+ *	(enum dpll_change_type)
>>+ **/
>>+enum dplla {
>>+	DPLLA_UNSPEC,
>>+	DPLLA_ID,
>>+	DPLLA_NAME,
>>+	DPLLA_MODE,
>>+	DPLLA_MODE_SUPPORTED,
>>+	DPLLA_SOURCE_PIN_IDX,
>>+	DPLLA_LOCK_STATUS,
>>+	DPLLA_TEMP,
>>+	DPLLA_CLOCK_ID,
>>+	DPLLA_CLOCK_CLASS,
>>+	DPLLA_FILTER,
>>+	DPLLA_PIN,
>>+	DPLLA_PIN_IDX,
>>+	DPLLA_PIN_DESCRIPTION,
>>+	DPLLA_PIN_TYPE,
>>+	DPLLA_PIN_SIGNAL_TYPE,
>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>+	DPLLA_PIN_CUSTOM_FREQ,
>>+	DPLLA_PIN_MODE,
>>+	DPLLA_PIN_MODE_SUPPORTED,
>>+	DPLLA_PIN_PRIO,
>>+	DPLLA_PIN_PARENT_IDX,
>>+	DPLLA_PIN_NETIFINDEX,
>
>I believe we cannot have this right now. The problem is, ifindexes may
>overlay between namespaces. So ifindex without namespace means nothing.
>I don't see how this can work from the dpll side.
>

I am a bit confused, as it seemed we already had an agreement on v4 discussion
on this. And now again you highligheted it with the same reasoning as
previously. Has anything changed on that matter?

>Lets assign dpll_pin pointer to netdev and expose it over RT netlink in
>a similar way devlink_port is exposed. That should be enough for the
>user to find a dpll instance for given netdev.
>
>It does not have to be part of this set strictly, but I would like to
>have it here, so the full picture could be seen.
>

A "full picture" is getting broken if we would go with another place to keep
the reference between pin and device that produces its signal.

Why don't we add an 'struct device *' argument for dpll_pin_alloc, create
new attribute with dev_name macro similary to the current name of dpll device
name, something like: DPLLA_PIN_RCLK_DEVICE (string).
This way any device (not only netdev) would be able to add a pin and point to
a device which produces its signal.

>
>
>>+	DPLLA_CHANGE_TYPE,
>>+	__DPLLA_MAX,
>>+};
>>+
>>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>>+
>>+/* dpll_lock_status - DPLL status provides information of device status
>>+ *
>>+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
>>+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid (or
>is in
>>+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
>>+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid
>signal
>>+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
>>+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a valid
>lock
>>+ *	or was forced by DPLL_MODE_HOLDOVER mode)
>>+ **/
>>+enum dpll_lock_status {
>>+	DPLL_LOCK_STATUS_UNSPEC,
>>+	DPLL_LOCK_STATUS_UNLOCKED,
>>+	DPLL_LOCK_STATUS_CALIBRATING,
>>+	DPLL_LOCK_STATUS_LOCKED,
>>+	DPLL_LOCK_STATUS_HOLDOVER,
>>+
>>+	__DPLL_LOCK_STATUS_MAX,
>>+};
>>+
>>+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
>>+
>>+/* dpll_pin_type - signal types
>>+ *
>>+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
>>+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
>>+ * @DPLL_PIN_TYPE_EXT - external source
>>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>>+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
>>+ **/
>>+enum dpll_pin_type {
>>+	DPLL_PIN_TYPE_UNSPEC,
>>+	DPLL_PIN_TYPE_MUX,
>>+	DPLL_PIN_TYPE_EXT,
>>+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>+	DPLL_PIN_TYPE_INT_OSCILLATOR,
>>+	DPLL_PIN_TYPE_GNSS,
>>+
>>+	__DPLL_PIN_TYPE_MAX,
>>+};
>>+
>>+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
>>+
>>+/* dpll_pin_signal_type - signal types
>>+ *
>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>
>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
>with HZ value directly? For example, supported freq:
>1, 10000000
>or:
>1, 1000
>
>freq set 10000000
>freq set 1
>
>Simple and easy.
>

AFAIR, we wanted to have most commonly used frequencies as enums + custom_freq
for some exotic ones (please note that there is also possible 2PPS, which is
0.5 Hz).
This was design decision we already agreed on.
The userspace shall get definite list of comonly used frequencies that can be
used with given HW, it clearly enums are good for this.

>
>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value
>defined
>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>+ **/
>>+enum dpll_pin_signal_type {
>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>+
>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>+};
>>+
>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>+
>>+/* dpll_pin_mode - available pin states
>>+ *
>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>>+ **/
>>+enum dpll_pin_mode {
>>+	DPLL_PIN_MODE_UNSPEC,
>>+	DPLL_PIN_MODE_CONNECTED,
>>+	DPLL_PIN_MODE_DISCONNECTED,
>>+	DPLL_PIN_MODE_SOURCE,
>>+	DPLL_PIN_MODE_OUTPUT,
>
>I don't follow. I see 2 enums:
>CONNECTED/DISCONNECTED
>SOURCE/OUTPUT
>why this is mangled together? How is it supposed to be working. Like a
>bitarray?
>

The userspace shouldn't worry about bits, it recieves a list of attributes.
For current/active mode: DPLLA_PIN_MODE, and for supported modes:
DPLLA_PIN_MODE_SUPPORTED. I.e.

	DPLLA_PIN_IDX			0
	DPLLA_PIN_MODE			1,3
	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4

The reason for existance of both DPLL_PIN_MODE_CONNECTED and
DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
and bitmask is not a way to go for userspace.


>
>>+
>>+	__DPLL_PIN_MODE_MAX,
>>+};
>>+
>>+#define DPLL_PIN_MODE_MAX (__DPLL_PIN_MODE_MAX - 1)
>>+
>>+/**
>>+ * dpll_event - Events of dpll generic netlink family
>>+ *
>>+ * @DPLL_EVENT_UNSPEC - invalid event type
>>+ * @DPLL_EVENT_DEVICE_CREATE - dpll device created
>>+ * @DPLL_EVENT_DEVICE_DELETE - dpll device deleted
>>+ * @DPLL_EVENT_DEVICE_CHANGE - attribute of dpll device or pin changed
>>+ **/
>>+enum dpll_event {
>>+	DPLL_EVENT_UNSPEC,
>>+	DPLL_EVENT_DEVICE_CREATE,
>>+	DPLL_EVENT_DEVICE_DELETE,
>>+	DPLL_EVENT_DEVICE_CHANGE,
>>+
>>+	__DPLL_EVENT_MAX,
>>+};
>>+
>>+#define DPLL_EVENT_MAX (__DPLL_EVENT_MAX - 1)
>>+
>>+/**
>>+ * dpll_change_type - values of events in case of device change event
>>+ * (DPLL_EVENT_DEVICE_CHANGE)
>>+ *
>>+ * @DPLL_CHANGE_UNSPEC - invalid event type
>>+ * @DPLL_CHANGE_MODE - mode changed
>>+ * @DPLL_CHANGE_LOCK_STATUS - lock status changed
>>+ * @DPLL_CHANGE_SOURCE_PIN - source pin changed,
>
>Why comma at the end? Same to couple of others
>

It's not needed here, will remove it.

>
>>+ * @DPLL_CHANGE_TEMP - temperature changed
>>+ * @DPLL_CHANGE_PIN_ADD - source pin added,
>>+ * @DPLL_CHANGE_PIN_DEL - source pin deleted,
>>+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
>>+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
>>+ * @DPLL_CHANGE_PIN_MODE - pin state changed
>>+ * @DPLL_CHANGE_PIN_PRIO - pin prio changed
>>+ **/
>>+enum dpll_event_change {
>>+	DPLL_CHANGE_UNSPEC,
>>+	DPLL_CHANGE_MODE,
>>+	DPLL_CHANGE_LOCK_STATUS,
>>+	DPLL_CHANGE_SOURCE_PIN,
>>+	DPLL_CHANGE_TEMP,
>>+	DPLL_CHANGE_PIN_ADD,
>>+	DPLL_CHANGE_PIN_DEL,
>>+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
>>+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
>>+	DPLL_CHANGE_PIN_MODE,
>>+	DPLL_CHANGE_PIN_PRIO,
>>+
>>+	__DPLL_CHANGE_MAX,
>>+};
>>+
>>+#define DPLL_CHANGE_MAX (__DPLL_CHANGE_MAX - 1)
>>+
>>+/**
>>+ * dpll_cmd - Commands supported by the dpll generic netlink family
>>+ *
>>+ * @DPLL_CMD_UNSPEC - invalid message type
>>+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes
>of
>>+ *	single dpll device and it's pins
>>+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
>>+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
>>+ **/
>>+enum dpll_cmd {
>>+	DPLL_CMD_UNSPEC,
>>+	DPLL_CMD_DEVICE_GET,
>>+	DPLL_CMD_DEVICE_SET,
>>+	DPLL_CMD_PIN_SET,
>
>Have pin get to get list of pins, then you can have 1:1 mapping to
>events and loose the enum dpll_event_change. This is the usual way to do
>stuff. Events have the same cmd and message format as get.
>

Sure, will do.

>
>>+
>>+	__DPLL_CMD_MAX,
>>+};
>>+
>>+#define DPLL_CMD_MAX (__DPLL_CMD_MAX - 1)
>>+
>>+/**
>>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate how
>>+ * dpll selects one of its sources to syntonize with a source.
>>+ *
>>+ * @DPLL_MODE_UNSPEC - invalid
>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request
>to dpll
>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>dpll
>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>
>Why does the user care which oscilator is run internally. It's freerun,
>isn't it? If you want to expose oscilator type, you should do it
>elsewhere.
>

In NCO user might change frequency of an output, in freerun cannot.

>
>>+ **/
>>+enum dpll_mode {
>>+	DPLL_MODE_UNSPEC,
>>+	DPLL_MODE_MANUAL,
>>+	DPLL_MODE_AUTOMATIC,
>>+	DPLL_MODE_HOLDOVER,
>>+	DPLL_MODE_FREERUN,
>>+	DPLL_MODE_NCO,
>>+
>>+	__DPLL_MODE_MAX,
>>+};
>>+
>>+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
>>+
>>+/**
>>+ * dpll_clock_class - enumerate quality class of a DPLL clock as
>specified in
>>+ * Recommendation ITU-T G.8273.2/Y.1368.2.
>>+ */
>>+enum dpll_clock_class {
>>+	DPLL_CLOCK_CLASS_UNSPEC,
>>+	DPLL_CLOCK_CLASS_A,
>>+	DPLL_CLOCK_CLASS_B,
>>+	DPLL_CLOCK_CLASS_C,
>>+
>>+	__DPLL_CLOCK_CLASS_MAX,
>>+};
>>+
>>+#define DPLL_CLOCK_CLASS_MAX (__DPLL_CLOCK_CLASS_MAX - 1)
>>+
>>+/**
>>+ * enum dpll_type - type of dpll, integer value of enum is embedded into
>>+ * name of DPLL device (DPLLA_NAME)
>
>Yeah, I really cannot understand why you think for a second that
>embedding an enum value into a name makes sense in this world :O
>

Probably was to lazy to add new attribute instead :)
Will add new attribute for it.


Thanks,
Arkadiusz

>
>>+ *
>>+ * @DPLL_TYPE_UNSPEC - unspecified
>>+ * @DPLL_TYPE_PPS - dpll produces Pulse-Per-Second signal
>>+ * @DPLL_TYPE_EEC - dpll drives the Ethernet Equipment Clock
>>+ */
>>+enum dpll_type {
>>+	DPLL_TYPE_UNSPEC,
>>+	DPLL_TYPE_PPS,
>>+	DPLL_TYPE_EEC,
>>+
>>+	__DPLL_TYPE_MAX
>>+};
>>+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
>>+
>>+#endif /* _UAPI_LINUX_DPLL_H */
>>--
>>2.30.2
>>

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

* RE: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-01-27 18:12       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-27 18:12 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, January 19, 2023 6:16 PM
>
>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>>DPLL framework is used to represent and configure DPLL devices
>>in systems. Each device that has DPLL and can configure sources
>>and outputs can use this framework. Netlink interface is used to
>>provide configuration data and to receive notification messages
>>about changes in the configuration or status of DPLL device.
>>Inputs and outputs of the DPLL device are represented as special
>>objects which could be dynamically added to and removed from DPLL
>>device.
>>
>>Co-developed-by: Milena Olech <milena.olech@intel.com>
>>Signed-off-by: Milena Olech <milena.olech@intel.com>
>>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Co-developed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
>>---
>> MAINTAINERS                 |    8 +
>> drivers/Kconfig             |    2 +
>> drivers/Makefile            |    1 +
>> drivers/dpll/Kconfig        |    7 +
>> drivers/dpll/Makefile       |    9 +
>> drivers/dpll/dpll_core.c    | 1010 +++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h    |  105 ++++
>> drivers/dpll/dpll_netlink.c |  883 ++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h |   24 +
>> include/linux/dpll.h        |  282 ++++++++++
>> include/uapi/linux/dpll.h   |  294 ++++++++++
>> 11 files changed, 2625 insertions(+)
>> create mode 100644 drivers/dpll/Kconfig
>> create mode 100644 drivers/dpll/Makefile
>> create mode 100644 drivers/dpll/dpll_core.c
>> create mode 100644 drivers/dpll/dpll_core.h
>> create mode 100644 drivers/dpll/dpll_netlink.c
>> create mode 100644 drivers/dpll/dpll_netlink.h
>> create mode 100644 include/linux/dpll.h
>> create mode 100644 include/uapi/linux/dpll.h
>>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index f82dd8d43c2b..de8a10b21ce8 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -6411,6 +6411,14 @@ F:
>>	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/swit
>>ch-drive
>> F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
>> F:	drivers/net/ethernet/freescale/dpaa2/dpsw*
>>
>>+DPLL CLOCK SUBSYSTEM
>>+M:	Vadim Fedorenko <vadfed@fb.com>
>>+L:	netdev@vger.kernel.org
>>+S:	Maintained
>>+F:	drivers/dpll/*
>>+F:	include/net/dpll.h
>>+F:	include/uapi/linux/dpll.h
>>+
>> DRBD DRIVER
>> M:	Philipp Reisner <philipp.reisner@linbit.com>
>> M:	Lars Ellenberg <lars.ellenberg@linbit.com>
>>diff --git a/drivers/Kconfig b/drivers/Kconfig
>>index 968bd0a6fd78..453df9e1210d 100644
>>--- a/drivers/Kconfig
>>+++ b/drivers/Kconfig
>>@@ -241,4 +241,6 @@ source "drivers/peci/Kconfig"
>>
>> source "drivers/hte/Kconfig"
>>
>>+source "drivers/dpll/Kconfig"
>>+
>> endmenu
>>diff --git a/drivers/Makefile b/drivers/Makefile
>>index bdf1c66141c9..7cbee58bc692 100644
>>--- a/drivers/Makefile
>>+++ b/drivers/Makefile
>>@@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER)		+= counter/
>> obj-$(CONFIG_MOST)		+= most/
>> obj-$(CONFIG_PECI)		+= peci/
>> obj-$(CONFIG_HTE)		+= hte/
>>+obj-$(CONFIG_DPLL)		+= dpll/
>>diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
>>new file mode 100644
>>index 000000000000..a4cae73f20d3
>>--- /dev/null
>>+++ b/drivers/dpll/Kconfig
>>@@ -0,0 +1,7 @@
>>+# SPDX-License-Identifier: GPL-2.0-only
>>+#
>>+# Generic DPLL drivers configuration
>>+#
>>+
>>+config DPLL
>>+  bool
>>diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>new file mode 100644
>>index 000000000000..b18cf848a010
>>--- /dev/null
>>+++ b/drivers/dpll/Makefile
>>@@ -0,0 +1,9 @@
>>+# SPDX-License-Identifier: GPL-2.0
>>+#
>>+# Makefile for DPLL drivers.
>>+#
>>+
>>+obj-$(CONFIG_DPLL)          += dpll_sys.o
>>+dpll_sys-y                  += dpll_core.o
>>+dpll_sys-y                  += dpll_netlink.o
>>+
>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>new file mode 100644
>>index 000000000000..fec534f17827
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.c
>>@@ -0,0 +1,1010 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ *  dpll_core.c - Generic DPLL Management class support.
>>+ *
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>+
>>+#include <linux/device.h>
>>+#include <linux/err.h>
>>+#include <linux/slab.h>
>>+#include <linux/string.h>
>>+
>>+#include "dpll_core.h"
>>+
>>+/**
>>+ * struct dpll_pin - structure for a dpll pin
>>+ * @idx:		unique id number for each pin
>>+ * @parent_pin:		parent pin
>>+ * @type:		type of the pin
>>+ * @ops:		operations this &dpll_pin supports
>>+ * @priv:		pointer to private information of owner
>>+ * @ref_dplls:		array of registered dplls
>>+ * @description:	name to distinguish the pin
>>+ */
>>+struct dpll_pin {
>>+	u32 idx;
>>+	struct dpll_pin *parent_pin;
>>+	enum dpll_pin_type type;
>>+	struct dpll_pin_ops *ops;
>>+	void *priv;
>>+	struct xarray ref_dplls;
>>+	char description[DPLL_PIN_DESC_LEN];
>>+};
>>+static DEFINE_MUTEX(dpll_device_xa_lock);
>>+
>>+static DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
>>+#define DPLL_REGISTERED		XA_MARK_1
>>+#define PIN_REGISTERED		XA_MARK_1
>
>DPLL_PIN_REGISTERED
>

Agree, will fix that.

>
>>+
>>+#define ASSERT_DPLL_REGISTERED(d)
>>\
>>+	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>>+#define ASSERT_DPLL_NOT_REGISTERED(d)
>>\
>>+	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>>+
>>+struct dpll_pin_ref {
>>+	struct dpll_device *dpll;
>>+	struct dpll_pin_ops *ops;
>>+	void *priv;
>>+};
>>+
>>+/**
>>+ * dpll_device_get_by_id - find dpll device by it's id
>>+ * @id: id of searched dpll
>>+ *
>>+ * Return: dpll_device struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_device_get_by_id(int id)
>>+{
>>+	struct dpll_device *dpll = NULL;
>>+
>>+	if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>>+		dpll = xa_load(&dpll_device_xa, id);
>>+
>>+	return dpll;
>>+}
>>+
>>+/**
>>+ * dpll_device_get_by_name - find dpll device by it's id
>>+ * @name: name of searched dpll
>>+ *
>>+ * Return: dpll_device struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_device_get_by_name(const char *name)
>>+{
>>+	struct dpll_device *dpll, *ret = NULL;
>>+	unsigned long index;
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>>+		if (!strcmp(dev_name(&dpll->dev), name)) {
>>+			ret = dpll;
>>+			break;
>>+		}
>>+	}
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+
>>+	return ret;
>>+}
>>+
>>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>
>Hmm, don't you want to put an owner module as an arg here as well? I
>don't see how could 2 modules sanely work with the same dpll instance.
>

Sorry, I don't get it.
How the driver that needs to find a dpll would know the owner module?
The idea of this is to let another driver instance to find a dpll device
already registered in OS.
The driver that is searching dpll device is not the same as the one that has
created the device, otherwise it wouldn't make any sense?

>
>>+						enum dpll_type type, u8 idx)
>>+{
>>+	struct dpll_device *dpll, *ret = NULL;
>>+	unsigned long index;
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>>+		if (dpll->clock_id == clock_id) {
>>+			if (dpll->type == type) {
>>+				if (dpll->dev_driver_idx == idx) {
>>+					ret = dpll;
>>+					break;
>>+				}
>>+			}
>>+		}
>>+	}
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
>>+
>>+static void dpll_device_release(struct device *dev)
>>+{
>>+	struct dpll_device *dpll;
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	dpll = to_dpll_device(dev);
>>+	dpll_device_unregister(dpll);
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+	dpll_device_free(dpll);
>>+}
>>+
>>+static struct class dpll_class = {
>>+	.name = "dpll",
>>+	.dev_release = dpll_device_release,
>
>Why do you want to do this? Why the driver cannot do
>dpll_device_unregister/free() manually. I think it makes things easier
>to read then to rely on dev garbage collector.
>

This was in the first version submitted by Vadim.
I think we can remove it, unless someone has different view?

>
>>+};
>>+
>>+struct dpll_device
>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>>+		   u8 dev_driver_idx, void *priv, struct device *parent)
>>+{
>>+	struct dpll_device *dpll;
>>+	int ret;
>>+
>>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>>+	if (!dpll)
>>+		return ERR_PTR(-ENOMEM);
>>+
>>+	mutex_init(&dpll->lock);
>>+	dpll->ops = ops;
>>+	dpll->dev.class = &dpll_class;
>>+	dpll->parent = parent;
>>+	dpll->type = type;
>>+	dpll->dev_driver_idx = dev_driver_idx;
>>+	dpll->clock_id = clock_id;
>>+	dpll->clock_class = clock_class;
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>>+		       xa_limit_16b, GFP_KERNEL);
>>+	if (ret)
>>+		goto error;
>>+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
>>+		     dev_driver_idx);
>>+	dpll->priv = priv;
>>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>
>What is exactly the point of using this mark?
>

I think this can be also removed now, as there is no separated alloc/register
for newly created dpll device.

>
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+	dpll_notify_device_create(dpll);
>>+
>>+	return dpll;
>>+
>>+error:
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+	kfree(dpll);
>>+	return ERR_PTR(ret);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_alloc);
>>+
>>+void dpll_device_free(struct dpll_device *dpll)
>>+{
>>+	WARN_ON_ONCE(!dpll);
>>+	WARN_ON_ONCE(!xa_empty(&dpll->pins));
>>+	xa_destroy(&dpll->pins);
>>+	mutex_destroy(&dpll->lock);
>>+	kfree(dpll);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_free);
>>+
>>+/**
>>+ * dpll_device_unregister - unregister dpll device
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Note: It does not free the memory
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll)
>>+{
>>+	ASSERT_DPLL_REGISTERED(dpll);
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	xa_erase(&dpll_device_xa, dpll->id);
>>+	dpll_notify_device_delete(dpll);
>
>Why do you need to hold the lock for notify?
>

Good catch, will move it out of critical section.

>
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_unregister);
>>+
>>+/**
>>+ * dpll_id - return dpll id
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: dpll id.
>>+ */
>>+u32 dpll_id(struct dpll_device *dpll)
>>+{
>>+	return dpll->id;
>>+}
>>+
>>+/**
>>+ * dpll_pin_idx - return index of a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Return: index of a pin or PIN_IDX_INVALID if not found.
>>+ */
>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)
>>+{
>>+	struct dpll_pin *pos;
>>+	unsigned long index;
>>+
>>+	xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED) {
>>+		if (pos == pin)
>
>What is the purpose of the lookup for the pin struct you pass as an arg?
>

Seems like no longer needed, will try to remove it.

>
>>+			return pin->idx;
>>+	}
>>+
>>+	return PIN_IDX_INVALID;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_idx);
>>+
>>+const char *dpll_dev_name(struct dpll_device *dpll)
>>+{
>>+	return dev_name(&dpll->dev);
>>+}
>>+
>>+struct dpll_pin *dpll_pin_alloc(const char *description,
>>+				const enum dpll_pin_type pin_type)
>
>s/pin_type/type/
>

Sure, will do.

>
>>+{
>>+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
>>+
>>+	if (!pin)
>>+		return ERR_PTR(-ENOMEM);
>>+	if (pin_type <= DPLL_PIN_TYPE_UNSPEC ||
>>+	    pin_type > DPLL_PIN_TYPE_MAX)
>>+		return ERR_PTR(-EINVAL);
>
>I think this check is not needed here. If driver is passing something
>else, it is buggy. Idk. If you decide to leave this, put it in WARN_ON
>

Sure, will do.

>
>>+
>>+	strncpy(pin->description, description, DPLL_PIN_DESC_LEN);
>
>kstrdup. Please treat the rest of the strings like that. No need to
>limit the string names.

Ok, will follow.

>
>
>>+	pin->description[DPLL_PIN_DESC_LEN - 1] = '\0';
>>+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
>>+	pin->type = pin_type;
>>+
>>+	return pin;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_alloc);
>>+
>>+static int dpll_alloc_pin_on_xa(struct xarray *pins, struct dpll_pin
>>*pin)
>>+{
>>+	struct dpll_pin *pos;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each(pins, index, pos) {
>>+		if (pos == pin ||
>>+		    !strncmp(pos->description, pin->description,
>>+			     DPLL_PIN_DESC_LEN))
>
>WARN_ON. The driver is buggy if it does something like this.

Sure, will do.

>
>
>>+			return -EEXIST;
>>+	}
>>+
>>+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
>>+	if (!ret)
>>+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);
>
>What is exactly the point of having this mark?
>

Think this could be now removed, we got rid of separated alloc/register for
dpll device.

>
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device
>>*dpll,
>>+			    struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	struct dpll_pin_ref *ref, *pos;
>>+	unsigned long index;
>>+	u32 idx;
>>+
>>+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
>>+	if (!ref)
>>+		return -ENOMEM;
>>+	ref->dpll = dpll;
>>+	ref->ops = ops;
>>+	ref->priv = priv;
>>+	if (!xa_empty(&pin->ref_dplls)) {
>
>Pointless check. Just do iterate.
>

Sure, will do.

>
>>+		xa_for_each(&pin->ref_dplls, index, pos) {
>>+			if (pos->dpll == ref->dpll)
>>+				return -EEXIST;
>>+		}
>>+	}
>>+
>>+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b,
>>GFP_KERNEL);
>>+}
>>+
>>+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device
>>*dpll)
>>+{
>>+	struct dpll_pin_ref *pos;
>>+	unsigned long index;
>>+
>>+	xa_for_each(&pin->ref_dplls, index, pos) {
>>+		if (pos->dpll == dpll) {
>>+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
>>+			break;
>>+		}
>>+	}
>>+}
>>+
>>+static int pin_deregister_from_xa(struct xarray *xa_pins, struct dpll_pin
>>*pin)
>
>1) dpll_ prefix

Sure, will do.

>2) "deregister" is odd name

Rodger that, will fix.

>3) why don't you have it next to dpll_alloc_pin_on_xa() as it is a
>   symmetric function?

Will do.

>4) Why exactly just xa_erase() would not do?

Might do, but need to rethink this :)

>
>>+{
>>+	struct dpll_pin *pos;
>>+	unsigned long index;
>>+
>>+	xa_for_each(xa_pins, index, pos) {
>>+		if (pos == pin) {
>>+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));
>
>You have an odd pattern of functions getting pin as an arg then
>doing lookup for the same pin. I have to be missing to see some
>black magic here :O
>

The black magic was done to target correct pin in case pin index differs
between dplls it was registered with. It would depend on the way shared pins
are going to be allocated.
If mixed pins approach is allowed (shared + non-shared pins) on any dpll, we
would end up in situation where pin index for the same physical pin on multiple
devices may be different, depending on registering pins order.

As desribed in below comments, I can see here one simple solution: allow kernel
module (which registers a pin with dpll) to control/assign pin index.
The kernel module would only need take care of them being unique, when
registers with first dpll - which seems not a problem. This way we would also
be albe to get rid of searching pin function (as indexes would be known for all
driver instances), different driver instances would use that index to share a
pin.
Also all the blackmagic like you described wouldn't be needed, thus simplifing
a dpll subsystem.

>
>>+			return 0;
>>+		}
>>+	}
>>+
>>+	return -ENXIO;
>>+}
>>+
>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	int ret;
>>+
>>+	if (!pin || !ops)
>>+		return -EINVAL;
>>+
>>+	mutex_lock(&dpll->lock);
>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>+	if (!ret) {
>>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>>+		if (ret)
>>+			pin_deregister_from_xa(&dpll->pins, pin);
>>+	}
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>+
>>+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins, int
>>idx)
>>+{
>>+	struct dpll_pin *pos;
>>+	unsigned long index;
>>+
>>+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
>>+		if (pos->idx == idx)
>>+			return pos;
>>+	}
>>+
>>+	return NULL;
>>+}
>>+
>>+/**
>>+ * dpll_pin_get_by_idx - find a pin by its index
>>+ * @dpll: dpll device pointer
>>+ * @idx: index of pin
>>+ *
>>+ * Allows multiple driver instances using one physical DPLL to find
>>+ * and share pin already registered with existing dpll device.
>>+ *
>>+ * Return: pointer if pin was found, NULL otherwise.
>>+ */
>>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
>>+{
>>+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
>>+}
>>+
>>+	struct dpll_pin
>>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char
>>*description)
>>+{
>>+	struct dpll_pin *pos, *pin = NULL;
>>+	unsigned long index;
>>+
>>+	xa_for_each(&dpll->pins, index, pos) {
>>+		if (!strncmp(pos->description, description,
>>+			     DPLL_PIN_DESC_LEN)) {
>>+			pin = pos;
>>+			break;
>>+		}
>>+	}
>>+
>>+	return pin;
>>+}
>>+
>>+int
>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>+			 struct dpll_device *dpll,
>>+			 const char *shared_pin_description,
>
>I don't follow why you need to pass the string. You have struct dpll_pin
>* in the driver. Pass that instead, avoid string to refer to kernel
>object. But this is something I wrote multiple times.
>

I wrote this so many times :) Separated driver instances doesn't have the pin
object pointer by default (unless they share it through some unwanted static/
global contatiners). They need to somehow target a pin, right now only unique
attributes on dpll/pin pair are a description and index.
Desription is a constant, index depends on the order of initialization and is
internal for a dpll device.
Previously there were a function to obtain a pin index by its description, then
register with obtained index, now this is merged into one function.

Altough I agree this is still not best aproach.
I will fix by: fallback to targeting a pin to be shared by its index, with one
slight design change, the pin index would have to be given by the driver
instance which registers it with the first dpll.
All the other separated driver instances which are using that pin will have to
know the index assigned to the pin that is going to be shared, which seems
like a best approach to fix this issue.

>
>>+			 struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	struct dpll_pin *pin;
>>+	int ret;
>>+
>>+	mutex_lock(&dpll_pin_owner->lock);
>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>+					  shared_pin_description);
>>+	if (!pin) {
>>+		ret = -EINVAL;
>>+		goto unlock;
>>+	}
>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>+unlock:
>>+	mutex_unlock(&dpll_pin_owner->lock);
>>+
>>+	return ret;
>
>I don't understand why there should be a separate function to register
>the shared pin. As I see it, there is a pin object that could be
>registered with 2 or more dpll devices. What about having:
>
>pin = dpll_pin_alloc(desc, type, ops, priv)
>dpll_pin_register(dpll_1, pin);
>dpll_pin_register(dpll_2, pin);
>dpll_pin_register(dpll_3, pin);
>

IMHO your example works already, but it would possible only if the same driver
instance initializes all dplls.
dpll_shared_pin_register is designed for driver instances without the pin
object.
 
>Then one pin will we in 3 xa_arrays for 3 dplls.
>

As we can see dpll_shared_pin_register is a fancy wrapper for
dpll_pin_register. So yeah, that's the point :) Just separated driver instances
sharing a pin are a issue, will fix with the approach described above (pin
index given by the registering driver instance).

>
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);
>>+
>>+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin)
>
>s/deregister/unregister. Be consistent in naming the functions.
>

Sure, will fix.

>
>>+{
>>+	int ret = 0;
>>+
>>+	if (xa_empty(&dpll->pins))
>>+		return -ENOENT;
>
>Remove this check
>

Sure, can do.

>>+
>>+	mutex_lock(&dpll->lock);
>>+	ret = pin_deregister_from_xa(&dpll->pins, pin);
>>+	if (!ret)
>>+		dpll_pin_ref_del(pin, dpll);
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_DEL);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_deregister);
>>+
>>+void dpll_pin_free(struct dpll_pin *pin)
>>+{
>>+	if (!xa_empty(&pin->ref_dplls))
>>+		return;
>>+
>>+	xa_destroy(&pin->ref_dplls);
>>+	kfree(pin);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_free);
>>+
>>+int dpll_muxed_pin_register(struct dpll_device *dpll,
>>+			    const char *parent_pin_description,
>
>Again, pass struct dpll_pin *parent, not a string.
>
>
>>+			    struct dpll_pin *pin,
>>+			    struct dpll_pin_ops *ops, void *priv)
>

Again, separated driver instances are the issue. Will fix with a parent pin
index given by registering driver, approach described above.

>Why this is a separate function? Why can't we have one function
>say __dpll_pin_register()
>which is called from
>dpll_pin_register() - parent == null
>or
>dpll_muxed_pin_register() - parent == valid parent pointer
>?
>

Sure, can try that. Altough as described above, a pin index instead of parent
pointer.

>
>
>>+{
>>+	struct dpll_pin *parent_pin;
>>+	int ret;
>>+
>>+	if (!parent_pin_description || !pin)
>>+		return -EINVAL;
>>+
>>+	mutex_lock(&dpll->lock);
>>+	parent_pin = dpll_pin_get_by_description(dpll,
>>parent_pin_description);
>>+	if (!parent_pin)
>>+		return -EINVAL;
>>+	if (parent_pin->type != DPLL_PIN_TYPE_MUX)
>>+		return -EPERM;
>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>+	if (!ret)
>>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>>+	if (!ret)
>>+		pin->parent_pin = parent_pin;
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
>>+
>>+/**
>>+ * dpll_pin_first - get first registered pin
>>+ * @dpll: registered dpll pointer
>>+ * @index: found pin index (out)
>>+ *
>>+ * Return: dpll_pin struct if found, NULL otherwise.
>>+ */
>>+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long
>>*index)
>>+{
>>+	*index = 0;
>>+
>>+	return xa_find(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>>+}
>>+
>>+/**
>>+ * dpll_pin_next - get next registered pin to the relative pin
>>+ * @dpll: registered dpll pointer
>>+ * @index: relative pin index (in and out)
>>+ *
>>+ * Return: dpll_pin struct if found, NULL otherwise.
>>+ */
>>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long
>>*index)
>>+{
>>+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>>+}
>>+
>>+/**
>>+ * dpll_first - get first registered dpll device
>>+ * @index: found dpll index (out)
>>+ *
>>+ * Return: dpll_device struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_first(unsigned long *index)
>>+{
>>+	*index = 0;
>>+
>>+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>>+}
>>+
>>+/**
>>+ * dpll_pin_next - get next registered dpll device to the relative pin
>>+ * @index: relative dpll index (in and out)
>>+ *
>>+ * Return: dpll_pin struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_next(unsigned long *index)
>>+{
>>+	return xa_find_after(&dpll_device_xa, index, LONG_MAX,
>>DPLL_REGISTERED);
>>+}
>>+
>>+static struct dpll_pin_ref
>>+*dpll_pin_find_ref(const struct dpll_device *dpll, const struct dpll_pin
>>*pin)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long index;
>>+
>>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>>+		if (ref->dpll != dpll)
>>+			continue;
>>+		else
>>+			return ref;
>>+	}
>>+
>>+	return NULL;
>>+}
>>+
>>+/**
>>+ * dpll_pin_type_get - get type of a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @type: on success - configured pin type
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got pin's type
>>+ * * negative - failed to get pin's type
>>+ */
>>+int dpll_pin_type_get(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin,
>>+		      enum dpll_pin_type *type)
>>+{
>>+	if (!pin)
>>+		return -ENODEV;
>>+	*type = pin->type;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * dpll_pin_signal_type_get - get signal type of a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @type: on success - configured signal type
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got signal type
>>+ * * negative - failed to obtain signal type
>>+ */
>>+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     enum dpll_pin_signal_type *type)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->signal_type_get)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->signal_type_get(ref->dpll, pin, type);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_signal_type_set - set signal type of a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @type: type to be set
>>+ *
>>+ * Return:
>>+ * * 0 - signal type set
>>+ * * negative - failed to set signal type
>>+ */
>>+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     const enum dpll_pin_signal_type type)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>>+		if (!ref->dpll)
>>+			return -EFAULT;
>>+		if (!ref || !ref->ops || !ref->ops->signal_type_set)
>>+			return -EOPNOTSUPP;
>>+		if (ref->dpll != dpll)
>>+			mutex_lock(&ref->dpll->lock);
>>+		ret = ref->ops->signal_type_set(ref->dpll, pin, type);
>>+		if (ref->dpll != dpll)
>>+			mutex_unlock(&ref->dpll->lock);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_signal_type_supported - check if signal type is supported on
>>a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @type: type being checked
>>+ * @supported: on success - if given signal type is supported
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got supported signal type
>>+ * * negative - failed to obtain supported signal type
>>+ */
>>+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>>+				   const struct dpll_pin *pin,
>>+				   const enum dpll_pin_signal_type type,
>>+				   bool *supported)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->signal_type_supported)
>>+		return -EOPNOTSUPP;
>>+	*supported = ref->ops->signal_type_supported(ref->dpll, pin, type);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * dpll_pin_mode_active - check if given mode is active on a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @mode: mode being checked
>>+ * @active: on success - if mode is active
>>+ *
>>+ * Return:
>>+ * * 0 - successfully checked if mode is active
>>+ * * negative - failed to check for active mode
>>+ */
>>+int dpll_pin_mode_active(const struct dpll_device *dpll,
>>+			  const struct dpll_pin *pin,
>>+			  const enum dpll_pin_mode mode,
>>+			  bool *active)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->mode_active)
>>+		return -EOPNOTSUPP;
>>+	*active = ref->ops->mode_active(ref->dpll, pin, mode);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * dpll_pin_mode_supported - check if given mode is supported on a pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @mode: mode being checked
>>+ * @supported: on success - if mode is supported
>>+ *
>>+ * Return:
>>+ * * 0 - successfully checked if mode is supported
>>+ * * negative - failed to check for supported mode
>>+ */
>>+int dpll_pin_mode_supported(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     const enum dpll_pin_mode mode,
>>+			     bool *supported)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->mode_supported)
>>+		return -EOPNOTSUPP;
>>+	*supported = ref->ops->mode_supported(ref->dpll, pin, mode);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * dpll_pin_mode_set - set pin's mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @mode: mode being set
>>+ *
>>+ * Return:
>>+ * * 0 - successfully set the mode
>>+ * * negative - failed to set the mode
>>+ */
>>+int dpll_pin_mode_set(const struct dpll_device *dpll,
>>+		       const struct dpll_pin *pin,
>>+		       const enum dpll_pin_mode mode)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>
>I don't understand why you need to call ops->mode_enable for all the
>dplls. The pin is shared, it's the single entity. One call should be
>enough? Why not? Same for other attrs, with exception of PRIO.
>

Single pin can connect multiple dplls. Most probably one call would be enough
if all dplls are controlled by one driver instance. But as example draw from:

https://lore.kernel.org/netdev/DM6PR11MB4657DC9A41A69B71A42DD22F9BFC9@DM6PR11MB4657.namprd11.prod.outlook.com/ 

This is not always the case. So we want to call this on all dplls and let
their drivers decide if there is need to take an action or not.

When registering a pin, kernel module can pass ops=NULL for a dpll that don't
require to be notified with callbacks about pin changes.

>
>>+		if (!ref)
>>+			return -ENODEV;
>>+		if (!ref->ops || !ref->ops->mode_enable)
>>+			return -EOPNOTSUPP;
>>+		if (ref->dpll != dpll)
>>+			mutex_lock(&ref->dpll->lock);
>>+		ret = ref->ops->mode_enable(ref->dpll, pin, mode);
>>+		if (ref->dpll != dpll)
>>+			mutex_unlock(&ref->dpll->lock);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_custom_freq_get - get pin's custom frequency
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @freq: on success - custom frequency of a pin
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got custom frequency
>>+ * * negative - failed to obtain custom frequency
>>+ */
>>+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin, u32 *freq)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>
>How this can happen?
>

Something would be seriously bugged. Think we can remove it.

>
>>+	if (!ref->ops || !ref->ops->custom_freq_get)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->custom_freq_get(ref->dpll, pin, freq);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_custom_freq_set - set pin's custom frequency
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @freq: custom frequency to be set
>>+ *
>>+ * Return:
>>+ * * 0 - successfully set custom frequency
>>+ * * negative - failed to set custom frequency
>>+ */
>>+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin, const u32 freq)
>>+{
>>+	enum dpll_pin_signal_type type;
>>+	struct dpll_pin_ref *ref;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each((struct xarray *)&pin->ref_dplls, index, ref) {
>>+		if (!ref)
>>+			return -ENODEV;
>>+		if (!ref->ops || !ref->ops->custom_freq_set ||
>>+		    !ref->ops->signal_type_get)
>>+			return -EOPNOTSUPP;
>>+		if (dpll != ref->dpll)
>>+			mutex_lock(&ref->dpll->lock);
>>+		ret = ref->ops->signal_type_get(dpll, pin, &type);
>>+		if (!ret && type == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ)
>>+			ret = ref->ops->custom_freq_set(ref->dpll, pin, freq);
>>+		if (dpll != ref->dpll)
>>+			mutex_unlock(&ref->dpll->lock);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_prio_get - get pin's prio on dpll
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @prio: on success - priority of a pin on a dpll
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got priority
>>+ * * negative - failed to obtain priority
>>+ */
>>+int dpll_pin_prio_get(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin, u32 *prio)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->prio_get)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->prio_get(ref->dpll, pin, prio);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_prio_set - set pin's prio on dpll
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @prio: priority of a pin to be set on a dpll
>>+ *
>>+ * Return:
>>+ * * 0 - successfully set priority
>>+ * * negative - failed to set the priority
>>+ */
>>+int dpll_pin_prio_set(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin, const u32 prio)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->prio_set)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->prio_set(ref->dpll, pin, prio);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_netifindex_get - get pin's netdev iterface index
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @netifindex: on success - index of a netdevice associated with pin
>>+ *
>>+ * Return:
>>+ * * 0 - successfully got netdev interface index
>>+ * * negative - failed to obtain netdev interface index
>>+ */
>>+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin,
>>+			    int *netifindex)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->net_if_idx_get)
>>+		return -EOPNOTSUPP;
>>+	ret = ref->ops->net_if_idx_get(ref->dpll, pin, netifindex);
>
>return right away.
>

Sure, can do.

>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_description - provide pin's description string
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Return: pointer to a description string.
>>+ */
>>+const char *dpll_pin_description(struct dpll_pin *pin)
>>+{
>>+	return pin->description;
>>+}
>>+
>>+/**
>>+ * dpll_pin_parent - provide pin's parent pin if available
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Return: pointer to aparent if found, NULL otherwise.
>>+ */
>>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)
>
>What exactly is the reason of having one line helpers to access struct
>fields for a struct which is known to the caller? Unneccesary
>boilerplate code. Please remove these. For pin and for dpll_device as
>well.
>

Actually dpll_pin is defined in dpll_core.c, so it is not known to the caller
yet. About dpll_device, yes it is known. And we need common approach here, thus
we need a fix. I know this is kernel code, full of hacks and performance related
bad-design stuff, so will fix as suggested.

>
>
>>+{
>>+	return pin->parent_pin;
>>+}
>>+
>>+/**
>>+ * dpll_mode_set - handler for dpll mode set
>>+ * @dpll: registered dpll pointer
>>+ * @mode: mode to be set
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode)
>>+{
>>+	int ret;
>>+
>>+	if (!dpll->ops || !dpll->ops)
>>+		return -EOPNOTSUPP;
>>+
>>+	ret = dpll->ops->mode_set(dpll, mode);
>
>return right away. You have this pattern on multiple places, please fix.
>

Sure, will fix.

>
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_source_idx_set - handler for selecting a dpll's source
>>+ * @dpll: registered dpll pointer
>>+ * @source_pin_idx: index of a source pin to e selected
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_source_idx_set(struct dpll_device *dpll, const u32
>>source_pin_idx)
>>+{
>>+	struct dpll_pin_ref *ref;
>>+	struct dpll_pin *pin;
>>+	int ret;
>>+
>>+	pin = dpll_pin_get_by_idx_from_xa(&dpll->pins, source_pin_idx);
>>+	if (!pin)
>>+		return -ENXIO;
>>+	ref = dpll_pin_find_ref(dpll, pin);
>>+	if (!ref || !ref->ops)
>>+		return -EFAULT;
>>+	if (!ref->ops->select)
>>+		return -ENODEV;
>
>ENODEV definitelly does not look like the correct value here.
>

Sure, will fix.

>
>>+	ret = ref->ops->select(ref->dpll, pin);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * dpll_lock - locks the dpll using internal mutex
>>+ * @dpll: registered dpll pointer
>>+ */
>>+void dpll_lock(struct dpll_device *dpll)
>>+{
>>+	mutex_lock(&dpll->lock);
>>+}
>>+
>>+/**
>>+ * dpll_unlock - unlocks the dpll using internal mutex
>>+ * @dpll: registered dpll pointer
>>+ */
>>+void dpll_unlock(struct dpll_device *dpll)
>>+{
>>+	mutex_unlock(&dpll->lock);
>>+}
>>+
>>+enum dpll_pin_type dpll_pin_type(const struct dpll_pin *pin)
>>+{
>>+	return pin->type;
>>+}
>>+
>>+void *dpll_priv(const struct dpll_device *dpll)
>>+{
>>+	return dpll->priv;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_priv);
>>+
>>+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin
>>*pin)
>>+{
>>+	struct dpll_pin_ref *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return NULL;
>>+
>>+	return ref->priv;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_priv);
>>+
>>+static int __init dpll_init(void)
>>+{
>>+	int ret;
>>+
>>+	ret = dpll_netlink_init();
>>+	if (ret)
>>+		goto error;
>>+
>>+	ret = class_register(&dpll_class);
>>+	if (ret)
>>+		goto unregister_netlink;
>>+
>>+	return 0;
>>+
>>+unregister_netlink:
>>+	dpll_netlink_finish();
>>+error:
>>+	mutex_destroy(&dpll_device_xa_lock);
>>+	return ret;
>>+}
>>+subsys_initcall(dpll_init);
>>diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
>>new file mode 100644
>>index 000000000000..b933d63b60c1
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.h
>>@@ -0,0 +1,105 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+#ifndef __DPLL_CORE_H__
>>+#define __DPLL_CORE_H__
>>+
>>+#include <linux/dpll.h>
>>+
>>+#include "dpll_netlink.h"
>>+
>>+#define to_dpll_device(_dev) \
>>+	container_of(_dev, struct dpll_device, dev)
>>+
>>+/**
>>+ * struct dpll_device - structure for a DPLL device
>>+ * @id:		unique id number for each device
>>+ * @dev:	struct device for this dpll device
>>+ * @parent:	parent device
>>+ * @ops:	operations this &dpll_device supports
>>+ * @lock:	mutex to serialize operations
>>+ * @type:	type of a dpll
>>+ * @priv:	pointer to private information of owner
>>+ * @pins:	list of pointers to pins registered with this dpll
>>+ * @clock_id:	unique identifier (clock_id) of a dpll
>>+ * @clock_class	quality class of a DPLL clock
>>+ * @dev_driver_idx: provided by driver for
>>+ */
>>+struct dpll_device {
>>+	u32 id;
>>+	struct device dev;
>>+	struct device *parent;
>>+	struct dpll_device_ops *ops;
>>+	struct mutex lock;
>>+	enum dpll_type type;
>>+	void *priv;
>>+	struct xarray pins;
>>+	u64 clock_id;
>>+	enum dpll_clock_class clock_class;
>>+	u8 dev_driver_idx;
>>+};
>>+
>>+#define for_each_pin_on_dpll(dpll, pin, i)			\
>>+	for (pin = dpll_pin_first(dpll, &i); pin != NULL;	\
>>+	     pin = dpll_pin_next(dpll, &i))
>
>What is the purpose for this macro and dpll_pin_first/next() helper?
>Why is this not equivalent to:
>xa_for_each_marked(&dpll->pins, index, pos, PIN_REGISTERED)
>

At some point of implementation, struct dpll_device was private for dpll_core.c
now we can probably have it simplified, will fix it.

>
>>+
>>+#define for_each_dpll(dpll, i)                         \
>>+	for (dpll = dpll_first(&i); dpll != NULL; dpll = dpll_next(&i))
>
>Same here, why this macro and helpers are needed?
>

The XA of DPLLs is private to dpll_core.c

>
>>+
>>+struct dpll_device *dpll_device_get_by_id(int id);
>>+
>>+struct dpll_device *dpll_device_get_by_name(const char *name);
>>+struct dpll_pin *dpll_pin_first(struct dpll_device *dpll, unsigned long
>*index);
>>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long
>*index);
>>+struct dpll_device *dpll_first(unsigned long *index);
>>+struct dpll_device *dpll_next(unsigned long *index);
>>+void dpll_device_unregister(struct dpll_device *dpll);
>>+u32 dpll_id(struct dpll_device *dpll);
>>+const char *dpll_dev_name(struct dpll_device *dpll);
>>+void dpll_lock(struct dpll_device *dpll);
>>+void dpll_unlock(struct dpll_device *dpll);
>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>>+int dpll_pin_type_get(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin,
>>+		      enum dpll_pin_type *type);
>>+int dpll_pin_signal_type_get(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     enum dpll_pin_signal_type *type);
>>+int dpll_pin_signal_type_set(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     const enum dpll_pin_signal_type type);
>>+int dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>>+				   const struct dpll_pin *pin,
>>+				   const enum dpll_pin_signal_type type,
>>+				   bool *supported);
>>+int dpll_pin_mode_active(const struct dpll_device *dpll,
>>+			 const struct dpll_pin *pin,
>>+			 const enum dpll_pin_mode mode,
>>+			 bool *active);
>>+int dpll_pin_mode_supported(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin,
>>+			    const enum dpll_pin_mode mode,
>>+			    bool *supported);
>>+int dpll_pin_mode_set(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin,
>>+		      const enum dpll_pin_mode mode);
>>+int dpll_pin_custom_freq_get(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin, u32 *freq);
>>+int dpll_pin_custom_freq_set(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin, const u32 freq);
>>+int dpll_pin_prio_get(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin, u32 *prio);
>>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx);
>>+int dpll_pin_prio_set(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin, const u32 prio);
>>+int dpll_pin_netifindex_get(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin,
>>+			    int *netifindex);
>>+const char *dpll_pin_description(struct dpll_pin *pin);
>>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin);
>>+int dpll_mode_set(struct dpll_device *dpll, const enum dpll_mode mode);
>>+int dpll_source_idx_set(struct dpll_device *dpll, const u32
>>source_pin_idx);
>>+
>>+#endif
>>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>>new file mode 100644
>>index 000000000000..91a1e5025ab2
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.c
>>@@ -0,0 +1,883 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ * Generic netlink for DPLL management framework
>>+ *
>>+ * Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ *
>>+ */
>>+#include <linux/module.h>
>>+#include <linux/kernel.h>
>>+#include <net/genetlink.h>
>>+#include "dpll_core.h"
>>+
>>+#include <uapi/linux/dpll.h>
>>+
>>+static const struct genl_multicast_group dpll_mcgrps[] = {
>>+	{ .name = DPLL_MONITOR_GROUP_NAME,  },
>>+};
>>+
>>+static const struct nla_policy dpll_cmd_device_get_policy[] = {
>>+	[DPLLA_ID]		= { .type = NLA_U32 },
>>+	[DPLLA_NAME]		= { .type = NLA_STRING,
>>+				    .len = DPLL_NAME_LEN },
>>+	[DPLLA_FILTER]		= { .type = NLA_U32 },
>>+};
>>+
>>+static const struct nla_policy dpll_cmd_device_set_policy[] = {
>>+	[DPLLA_ID]		= { .type = NLA_U32 },
>>+	[DPLLA_NAME]		= { .type = NLA_STRING,
>>+				    .len = DPLL_NAME_LEN },
>>+	[DPLLA_MODE]		= { .type = NLA_U32 },
>>+	[DPLLA_SOURCE_PIN_IDX]	= { .type = NLA_U32 },
>>+};
>>+
>>+static const struct nla_policy dpll_cmd_pin_set_policy[] = {
>>+	[DPLLA_ID]		= { .type = NLA_U32 },
>>+	[DPLLA_PIN_IDX]		= { .type = NLA_U32 },
>>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>>+	[DPLLA_PIN_MODE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>>+};
>>+
>>+struct dpll_dump_ctx {
>>+	int dump_filter;
>>+};
>>+
>>+static struct genl_family dpll_gnl_family;
>>+
>>+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback
>*cb)
>>+{
>>+	return (struct dpll_dump_ctx *)cb->ctx;
>>+}
>>+
>>+static int dpll_msg_add_id(struct sk_buff *msg, u32 id)
>>+{
>>+	if (nla_put_u32(msg, DPLLA_ID, id))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_name(struct sk_buff *msg, const char *name)
>>+{
>>+	if (nla_put_string(msg, DPLLA_NAME, name))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int __dpll_msg_add_mode(struct sk_buff *msg, enum dplla msg_type,
>>+			       enum dpll_mode mode)
>>+{
>>+	if (nla_put_s32(msg, msg_type, mode))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_mode(struct sk_buff *msg, const struct dpll_device *dpll)
>>+{
>>+	enum dpll_mode m;
>>+	int ret;
>>+
>>+	if (!dpll->ops->mode_get)
>>+		return 0;
>>+	ret = dpll->ops->mode_get(dpll, &m);
>>+	if (ret)
>>+		return ret;
>>+
>>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>>+}
>>+
>>+static int
>>+dpll_msg_add_modes_supported(struct sk_buff *msg,
>>+			     const struct dpll_device *dpll)
>>+{
>>+	enum dpll_mode i;
>>+	int ret = 0;
>>+
>>+	if (!dpll->ops->mode_supported)
>>+		return ret;
>>+
>>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>>+		if (dpll->ops->mode_supported(dpll, i)) {
>>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_msg_add_clock_id(struct sk_buff *msg,
>>+				 const struct dpll_device *dpll)
>>+{
>>+	if (nla_put_64bit(msg, DPLLA_CLOCK_ID, sizeof(dpll->clock_id),
>>+			  &dpll->clock_id, 0))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_clock_class(struct sk_buff *msg,
>>+				    const struct dpll_device *dpll)
>>+{
>>+	if (nla_put_s32(msg, DPLLA_CLOCK_CLASS, dpll->clock_class))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_device *dpll)
>>+{
>>+	u32 source_idx;
>>+	int ret;
>>+
>>+	if (!dpll->ops->source_pin_idx_get)
>>+		return 0;
>>+	ret = dpll->ops->source_pin_idx_get(dpll, &source_idx);
>>+	if (ret)
>>+		return ret;
>>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct
>>dpll_device *dpll)
>>+{
>>+	enum dpll_lock_status s;
>>+	int ret;
>>+
>>+	if (!dpll->ops->lock_status_get)
>>+		return 0;
>>+	ret = dpll->ops->lock_status_get(dpll, &s);
>>+	if (ret)
>>+		return ret;
>>+	if (nla_put_s32(msg, DPLLA_LOCK_STATUS, s))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device
>*dpll)
>>+{
>>+	s32 temp;
>>+	int ret;
>>+
>>+	if (!dpll->ops->temp_get)
>>+		return 0;
>>+	ret = dpll->ops->temp_get(dpll, &temp);
>>+	if (ret)
>>+		return ret;
>>+	if (nla_put_u32(msg, DPLLA_TEMP, temp))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_idx(struct sk_buff *msg, u32 pin_idx)
>>+{
>>+	if (nla_put_u32(msg, DPLLA_PIN_IDX, pin_idx))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_description(struct sk_buff *msg,
>>+					const char *description)
>>+{
>>+	if (nla_put_string(msg, DPLLA_PIN_DESCRIPTION, description))
>
>I don't understand the reason to have these helpers. I said that before,
>just call nla_put_* directly and avoid these unnecessary boilerplate
>unctions.
>

Sure, previously there were some parts reused, rest was for consistency,
I think we can remove all of them in next version.

>
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_parent_idx(struct sk_buff *msg, u32
>>parent_idx)
>>+{
>>+	if (nla_put_u32(msg, DPLLA_PIN_PARENT_IDX, parent_idx))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_device
>>*dpll,
>>+		      const struct dpll_pin *pin)
>>+{
>>+	enum dpll_pin_type t;
>>+
>>+	if (dpll_pin_type_get(dpll, pin, &t))
>>+		return 0;
>>+
>>+	if (nla_put_s32(msg, DPLLA_PIN_TYPE, t))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int __dpll_msg_add_pin_signal_type(struct sk_buff *msg,
>>+					  enum dplla attr,
>>+					  enum dpll_pin_signal_type type)
>>+{
>>+	if (nla_put_s32(msg, attr, type))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_signal_type(struct sk_buff *msg,
>>+					const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin)
>>+{
>>+	enum dpll_pin_signal_type t;
>
>s/t/type/
>

Sure, gonna fix.

>
>>+	int ret;
>>+
>>+	if (dpll_pin_signal_type_get(dpll, pin, &t))
>
>Why don't you propagate the error value?
>

The driver which have not implemented the callback might do it on purpose,
I will fix it by narrowing return 0 here only for missing callback.

>
>>+		return 0;
>>+	ret = __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>>+	if (ret)
>>+		return ret;
>>+
>>+	if (t == DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ) {
>>+		u32 freq;
>>+
>>+		if (dpll_pin_custom_freq_get(dpll, pin, &freq))
>>+			return 0;
>>+		if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>>+			return -EMSGSIZE;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>>+					const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin)
>>+{
>>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>>+	enum dpll_pin_signal_type i;
>>+	bool supported;
>>+
>>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>>+		if (dpll_pin_signal_type_supported(dpll, pin, i, &supported))
>>+			continue;
>>+		if (supported) {
>>+			int ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>>+
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>+				   const struct dpll_device *dpll,
>>+				   const struct dpll_pin *pin)
>>+{
>>+	enum dpll_pin_mode i;
>>+	bool active;
>>+
>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>+			return 0;
>>+		if (active)
>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>
>Why this is signed?
>

Because enums are signed.

>
>>+				return -EMSGSIZE;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_modes_supported(struct sk_buff *msg,
>>+					     const struct dpll_device *dpll,
>>+					     const struct dpll_pin *pin)
>>+{
>>+	enum dpll_pin_mode i;
>>+	bool supported;
>>+
>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>+		if (dpll_pin_mode_supported(dpll, pin, i, &supported))
>>+			return 0;
>>+		if (supported)
>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE_SUPPORTED, i))
>
>Here too. Please check the rest, you should not need to put signed
>values.
>

Enums are signed, don't understand why you want to mix types?

>
>>+				return -EMSGSIZE;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_device
>*dpll,
>>+		      const struct dpll_pin *pin)
>>+{
>>+	u32 prio;
>>+
>>+	if (dpll_pin_prio_get(dpll, pin, &prio))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_PIN_PRIO, prio))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct dpll_device
>>*dpll,
>>+			    const struct dpll_pin *pin)
>>+{
>>+	int netifindex;
>>+
>>+	if (dpll_pin_netifindex_get(dpll, pin, &netifindex))
>>+		return 0;
>>+	if (nla_put_s32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+__dpll_cmd_device_dump_one(struct sk_buff *msg, struct dpll_device *dpll)
>>+{
>>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
>>+
>>+	return ret;
>>+}
>>+
>>+static int
>>+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_device *dpll,
>>+			struct dpll_pin *pin)
>>+{
>>+	struct dpll_pin *parent = NULL;
>>+	int ret;
>>+
>>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_description(msg, dpll_pin_description(pin));
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_type(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	parent = dpll_pin_parent(pin);
>>+	if (parent) {
>>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>>+								    parent));
>>+		if (ret)
>>+			return ret;
>>+	}
>>+	ret = dpll_msg_add_pin_signal_type(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_signal_types_supported(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_modes(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_modes_supported(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_prio(msg, dpll, pin);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_pin_netifindex(msg, dpll, pin);
>>+
>>+	return ret;
>>+}
>>+
>>+static int __dpll_cmd_dump_pins(struct sk_buff *msg, struct dpll_device
>>*dpll)
>>+{
>>+	struct dpll_pin *pin;
>>+	struct nlattr *attr;
>>+	unsigned long i;
>>+	int ret = 0;
>>+
>>+	for_each_pin_on_dpll(dpll, pin, i) {
>>+		attr = nla_nest_start(msg, DPLLA_PIN);
>>+		if (!attr) {
>>+			ret = -EMSGSIZE;
>>+			goto nest_cancel;
>>+		}
>>+		ret = __dpll_cmd_pin_dump_one(msg, dpll, pin);
>>+		if (ret)
>>+			goto nest_cancel;
>>+		nla_nest_end(msg, attr);
>>+	}
>>+
>>+	return ret;
>>+
>>+nest_cancel:
>>+	nla_nest_cancel(msg, attr);
>>+	return ret;
>>+}
>>+
>>+static int
>>+__dpll_cmd_dump_status(struct sk_buff *msg, struct dpll_device *dpll)
>>+{
>>+	int ret = dpll_msg_add_source_pin(msg, dpll);
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_temp(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_lock_status(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_mode(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_modes_supported(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_clock_id(msg, dpll);
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_clock_class(msg, dpll);
>>+
>>+	return ret;
>>+}
>>+
>>+static int
>>+dpll_device_dump_one(struct dpll_device *dpll, struct sk_buff *msg,
>>+		     int dump_filter)
>>+{
>>+	int ret;
>>+
>>+	dpll_lock(dpll);
>>+	ret = __dpll_cmd_device_dump_one(msg, dpll);
>>+	if (ret)
>>+		goto out_unlock;
>>+
>>+	if (dump_filter & DPLL_FILTER_STATUS) {
>>+		ret = __dpll_cmd_dump_status(msg, dpll);
>>+		if (ret) {
>>+			if (ret != -EMSGSIZE)
>>+				ret = -EAGAIN;
>>+			goto out_unlock;
>>+		}
>>+	}
>>+	if (dump_filter & DPLL_FILTER_PINS)
>>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>>+	dpll_unlock(dpll);
>>+
>>+	return ret;
>>+out_unlock:
>>+	dpll_unlock(dpll);
>>+	return ret;
>>+}
>>+
>>+static int
>>+dpll_pin_set_from_nlattr(struct dpll_device *dpll,
>>+			 struct dpll_pin *pin, struct genl_info *info)
>>+{
>>+	enum dpll_pin_signal_type st;
>>+	enum dpll_pin_mode mode;
>>+	struct nlattr *a;
>>+	int rem, ret = 0;
>>+	u32 prio, freq;
>>+
>>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>+			  genlmsg_len(info->genlhdr), rem) {
>>+		switch (nla_type(a)) {
>>+		case DPLLA_PIN_SIGNAL_TYPE:
>>+			st = nla_get_s32(a);
>>+			ret = dpll_pin_signal_type_set(dpll, pin, st);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_CUSTOM_FREQ:
>>+			freq = nla_get_u32(a);
>>+			ret = dpll_pin_custom_freq_set(dpll, pin, freq);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_MODE:
>>+			mode = nla_get_s32(a);
>>+			ret = dpll_pin_mode_set(dpll, pin, mode);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_PRIO:
>>+			prio = nla_get_u32(a);
>>+			ret = dpll_pin_prio_set(dpll, pin, prio);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		default:
>>+			break;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_cmd_pin_set(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	struct nlattr **attrs = info->attrs;
>>+	struct dpll_pin *pin;
>>+	int pin_id;
>>+
>>+	if (!attrs[DPLLA_PIN_IDX])
>>+		return -EINVAL;
>>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>>+	dpll_lock(dpll);
>>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>>+	dpll_unlock(dpll);
>>+	if (!pin)
>>+		return -ENODEV;
>>+	return dpll_pin_set_from_nlattr(dpll, pin, info);
>>+}
>>+
>>+enum dpll_mode dpll_msg_read_mode(struct nlattr *a)
>>+{
>>+	return nla_get_s32(a);
>>+}
>>+
>>+u32 dpll_msg_read_source_pin_id(struct nlattr *a)
>>+{
>>+	return nla_get_u32(a);
>>+}
>>+
>>+static int
>>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>>+{
>>+	enum dpll_mode m;
>>+	struct nlattr *a;
>>+	int rem, ret = 0;
>>+	u32 source_pin;
>>+
>>+	nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>+			  genlmsg_len(info->genlhdr), rem) {
>>+		switch (nla_type(a)) {
>>+		case DPLLA_MODE:
>>+			m = dpll_msg_read_mode(a);
>>+
>>+			ret = dpll_mode_set(dpll, m);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_SOURCE_PIN_IDX:
>>+			source_pin = dpll_msg_read_source_pin_id(a);
>>+
>>+			ret = dpll_source_idx_set(dpll, source_pin);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		default:
>>+			break;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_cmd_device_set(struct sk_buff *skb, struct genl_info
>>*info)
>>+{
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+
>>+	return dpll_set_from_nlattr(dpll, info);
>>+}
>>+
>>+static int
>>+dpll_cmd_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
>>+{
>>+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>>+	struct dpll_device *dpll;
>>+	struct nlattr *hdr;
>>+	unsigned long i;
>>+	int ret;
>>+
>>+	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh-
>>nlmsg_seq,
>>+			  &dpll_gnl_family, 0, DPLL_CMD_DEVICE_GET);
>>+	if (!hdr)
>>+		return -EMSGSIZE;
>>+
>>+	for_each_dpll(dpll, i) {
>>+		ret = dpll_device_dump_one(dpll, skb, ctx->dump_filter);
>>+		if (ret)
>>+			break;
>>+	}
>>+
>>+	if (ret)
>>+		genlmsg_cancel(skb, hdr);
>>+	else
>>+		genlmsg_end(skb, hdr);
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_cmd_device_get(struct sk_buff *skb, struct genl_info
>>*info)
>>+{
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	struct nlattr **attrs = info->attrs;
>>+	struct sk_buff *msg;
>>+	int dump_filter = 0;
>>+	struct nlattr *hdr;
>>+	int ret;
>>+
>>+	if (attrs[DPLLA_FILTER])
>>+		dump_filter = nla_get_s32(attrs[DPLLA_FILTER]);
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+	hdr = genlmsg_put_reply(msg, info, &dpll_gnl_family, 0,
>>+				DPLL_CMD_DEVICE_GET);
>>+	if (!hdr)
>>+		return -EMSGSIZE;
>>+
>>+	ret = dpll_device_dump_one(dpll, msg, dump_filter);
>>+	if (ret)
>>+		goto out_free_msg;
>>+	genlmsg_end(msg, hdr);
>>+
>>+	return genlmsg_reply(msg, info);
>>+
>>+out_free_msg:
>>+	nlmsg_free(msg);
>>+	return ret;
>>+
>>+}
>>+
>>+static int dpll_cmd_device_get_start(struct netlink_callback *cb)
>>+{
>>+	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
>>+	struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>>+	struct nlattr *attr = info->attrs[DPLLA_FILTER];
>>+
>>+	if (attr)
>>+		ctx->dump_filter = nla_get_s32(attr);
>>+	else
>>+		ctx->dump_filter = 0;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff
>*skb,
>>+			 struct genl_info *info)
>>+{
>>+	struct dpll_device *dpll_id = NULL, *dpll_name = NULL;
>>+
>>+	if (!info->attrs[DPLLA_ID] &&
>>+	    !info->attrs[DPLLA_NAME])
>>+		return -EINVAL;
>>+
>>+	if (info->attrs[DPLLA_ID]) {
>>+		u32 id = nla_get_u32(info->attrs[DPLLA_ID]);
>>+
>>+		dpll_id = dpll_device_get_by_id(id);
>>+		if (!dpll_id)
>>+			return -ENODEV;
>>+		info->user_ptr[0] = dpll_id;
>>+	}
>>+	if (info->attrs[DPLLA_NAME]) {
>>+		const char *name = nla_data(info->attrs[DPLLA_NAME]);
>>+
>>+		dpll_name = dpll_device_get_by_name(name);
>>+		if (!dpll_name)
>>+			return -ENODEV;
>>+
>>+		if (dpll_id && dpll_name != dpll_id)
>>+			return -EINVAL;
>>+		info->user_ptr[0] = dpll_name;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static const struct genl_ops dpll_ops[] = {
>>+	{
>>+		.cmd	= DPLL_CMD_DEVICE_GET,
>>+		.flags  = GENL_UNS_ADMIN_PERM,
>>+		.start	= dpll_cmd_device_get_start,
>>+		.dumpit	= dpll_cmd_device_dump,
>>+		.doit	= dpll_cmd_device_get,
>>+		.policy	= dpll_cmd_device_get_policy,
>>+		.maxattr = ARRAY_SIZE(dpll_cmd_device_get_policy) - 1,
>>+	},
>>+	{
>>+		.cmd	= DPLL_CMD_DEVICE_SET,
>>+		.flags	= GENL_UNS_ADMIN_PERM,
>>+		.doit	= dpll_cmd_device_set,
>>+		.policy	= dpll_cmd_device_set_policy,
>>+		.maxattr = ARRAY_SIZE(dpll_cmd_device_set_policy) - 1,
>>+	},
>>+	{
>>+		.cmd	= DPLL_CMD_PIN_SET,
>>+		.flags	= GENL_UNS_ADMIN_PERM,
>>+		.doit	= dpll_cmd_pin_set,
>>+		.policy	= dpll_cmd_pin_set_policy,
>>+		.maxattr = ARRAY_SIZE(dpll_cmd_pin_set_policy) - 1,
>>+	},
>>+};
>>+
>>+static struct genl_family dpll_family __ro_after_init = {
>>+	.hdrsize	= 0,
>
>No need for static.
>

Sorry, don't get it, why it shall be non-static?

>
>>+	.name		= DPLL_FAMILY_NAME,
>>+	.version	= DPLL_VERSION,
>>+	.ops		= dpll_ops,
>>+	.n_ops		= ARRAY_SIZE(dpll_ops),
>>+	.mcgrps		= dpll_mcgrps,
>>+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
>>+	.pre_doit	= dpll_pre_doit,
>>+	.parallel_ops   = true,
>>+};
>>+
>>+static int dpll_event_device_id(struct sk_buff *msg, struct dpll_device
>>*dpll)
>>+{
>>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_name(msg, dpll_dev_name(dpll));
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_event_device_change(struct sk_buff *msg,
>>+				    struct dpll_device *dpll,
>>+				    struct dpll_pin *pin,
>>+				    enum dpll_event_change event)
>>+{
>>+	int ret = dpll_msg_add_id(msg, dpll_id(dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = nla_put_s32(msg, DPLLA_CHANGE_TYPE, event);
>>+	if (ret)
>>+		return ret;
>>+	switch (event)	{
>>+	case DPLL_CHANGE_PIN_ADD:
>>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>>+	case DPLL_CHANGE_PIN_MODE:
>>+	case DPLL_CHANGE_PIN_PRIO:
>>+		ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>>+		break;
>>+	default:
>>+		break;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/*
>>+ * Generic netlink DPLL event encoding
>>+ */
>>+static int dpll_send_event_create(enum dpll_event event,
>>+				  struct dpll_device *dpll)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+
>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>>+	if (!hdr)
>>+		goto out_free_msg;
>>+
>>+	ret = dpll_event_device_id(msg, dpll);
>>+	if (ret)
>>+		goto out_cancel_msg;
>>+	genlmsg_end(msg, hdr);
>>+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
>>+
>>+	return 0;
>>+
>>+out_cancel_msg:
>>+	genlmsg_cancel(msg, hdr);
>>+out_free_msg:
>>+	nlmsg_free(msg);
>>+
>>+	return ret;
>>+}
>>+
>>+/*
>>+ * Generic netlink DPLL event encoding
>>+ */
>>+static int dpll_send_event_change(struct dpll_device *dpll,
>>+				  struct dpll_pin *pin,
>>+				  enum dpll_event_change event)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+
>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0,
>>DPLL_EVENT_DEVICE_CHANGE);
>>+	if (!hdr)
>>+		goto out_free_msg;
>>+
>>+	ret = dpll_event_device_change(msg, dpll, pin, event);
>>+	if (ret)
>>+		goto out_cancel_msg;
>>+	genlmsg_end(msg, hdr);
>>+	genlmsg_multicast(&dpll_family, msg, 0, 0, GFP_KERNEL);
>>+
>>+	return 0;
>>+
>>+out_cancel_msg:
>>+	genlmsg_cancel(msg, hdr);
>>+out_free_msg:
>>+	nlmsg_free(msg);
>>+
>>+	return ret;
>>+}
>>+
>>+int dpll_notify_device_create(struct dpll_device *dpll)
>>+{
>>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_CREATE, dpll);
>>+}
>>+
>>+int dpll_notify_device_delete(struct dpll_device *dpll)
>>+{
>>+	return dpll_send_event_create(DPLL_EVENT_DEVICE_DELETE, dpll);
>>+}
>>+
>>+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change
>>event)
>>+{
>>+	return dpll_send_event_change(dpll, NULL, event);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_notify);
>>+
>>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		    enum dpll_event_change event)
>>+{
>>+	return dpll_send_event_change(dpll, pin, event);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_notify);
>>+
>>+int __init dpll_netlink_init(void)
>>+{
>>+	return genl_register_family(&dpll_family);
>>+}
>>+
>>+void dpll_netlink_finish(void)
>>+{
>>+	genl_unregister_family(&dpll_family);
>>+}
>>+
>>+void __exit dpll_netlink_fini(void)
>>+{
>>+	dpll_netlink_finish();
>>+}
>>diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
>>new file mode 100644
>>index 000000000000..8e50b2493027
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.h
>>@@ -0,0 +1,24 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+/**
>>+ * dpll_notify_device_create - notify that the device has been created
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_notify_device_create(struct dpll_device *dpll);
>>+
>>+
>>+/**
>>+ * dpll_notify_device_delete - notify that the device has been deleted
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_notify_device_delete(struct dpll_device *dpll);
>>+
>>+int __init dpll_netlink_init(void);
>>+void dpll_netlink_finish(void);
>>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>>new file mode 100644
>>index 000000000000..fcba46ea1b7b
>>--- /dev/null
>>+++ b/include/linux/dpll.h
>>@@ -0,0 +1,282 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ *  Copyright (c) 2021 Meta Platforms, Inc. and affiliates
>>+ */
>>+
>>+#ifndef __DPLL_H__
>>+#define __DPLL_H__
>>+
>>+#include <uapi/linux/dpll.h>
>>+#include <linux/device.h>
>>+
>>+struct dpll_device;
>>+struct dpll_pin;
>>+
>>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>>+
>>+struct dpll_device_ops {
>>+	int (*mode_get)(const struct dpll_device *dpll, enum dpll_mode
>>*mode);
>>+	int (*mode_set)(const struct dpll_device *dpll,
>>+			const enum dpll_mode mode);
>>+	bool (*mode_supported)(const struct dpll_device *dpll,
>>+			       const enum dpll_mode mode);
>>+	int (*source_pin_idx_get)(const struct dpll_device *dpll,
>>+				  u32 *pin_idx);
>>+	int (*lock_status_get)(const struct dpll_device *dpll,
>>+			       enum dpll_lock_status *status);
>>+	int (*temp_get)(const struct dpll_device *dpll, s32 *temp);
>>+};
>>+
>>+struct dpll_pin_ops {
>>+	int (*signal_type_get)(const struct dpll_device *dpll,
>>+			       const struct dpll_pin *pin,
>>+			       enum dpll_pin_signal_type *type);
>>+	int (*signal_type_set)(const struct dpll_device *dpll,
>>+			       const struct dpll_pin *pin,
>>+			       const enum dpll_pin_signal_type type);
>>+	bool (*signal_type_supported)(const struct dpll_device *dpll,
>>+				      const struct dpll_pin *pin,
>>+				      const enum dpll_pin_signal_type type);
>>+	int (*custom_freq_set)(const struct dpll_device *dpll,
>>+			       const struct dpll_pin *pin,
>>+			       const u32 custom_freq);
>>+	int (*custom_freq_get)(const struct dpll_device *dpll,
>>+			       const struct dpll_pin *pin,
>>+			       u32 *custom_freq);
>>+	bool (*mode_active)(const struct dpll_device *dpll,
>>+			     const struct dpll_pin *pin,
>>+			     const enum dpll_pin_mode mode);
>>+	int (*mode_enable)(const struct dpll_device *dpll,
>>+			    const struct dpll_pin *pin,
>>+			    const enum dpll_pin_mode mode);
>>+	bool (*mode_supported)(const struct dpll_device *dpll,
>>+				const struct dpll_pin *pin,
>>+				const enum dpll_pin_mode mode);
>>+	int (*prio_get)(const struct dpll_device *dpll,
>>+			const struct dpll_pin *pin,
>>+			u32 *prio);
>>+	int (*prio_set)(const struct dpll_device *dpll,
>>+			const struct dpll_pin *pin,
>>+			const u32 prio);
>>+	int (*net_if_idx_get)(const struct dpll_device *dpll,
>>+			      const struct dpll_pin *pin,
>>+			      int *net_if_idx);
>>+	int (*select)(const struct dpll_device *dpll,
>>+		      const struct dpll_pin *pin);
>
>Could you please pass extack to all of the ops? I think it is important
>to give the user the meaningfull error message from the start.
>

Sure, will add that.

>
>>+};
>>+
>>+/**
>>+ * dpll_device_alloc - allocate memory for a new dpll_device object
>>+ * @ops: pointer to dpll operations structure
>>+ * @type: type of a dpll being allocated
>>+ * @clock_id: a system unique number for a device
>>+ * @clock_class: quality class of a DPLL clock
>>+ * @dev_driver_idx: index of dpll device on parent device
>>+ * @priv: private data of a registerer
>>+ * @parent: device structure of a module registering dpll device
>>+ *
>>+ * Allocate memory for a new dpll and initialize it with its type, name,
>>+ * callbacks and private data pointer.
>>+ *
>>+ * Name is generated based on: parent driver, type and dev_driver_idx.
>>+ * Finding allocated and registered dpll device is also possible with
>>+ * the: clock_id, type and dev_driver_idx. This way dpll device can be
>>+ * shared by multiple instances of a device driver.
>>+ *
>>+ * Returns:
>>+ * * pointer to initialized dpll - success
>>+ * * NULL - memory allocation fail
>>+ */
>>+struct dpll_device
>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>>+		   u8 dev_driver_idx, void *priv, struct device *parent);
>>+
>>+/**
>>+ * dpll_device_unregister - unregister registered dpll
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Unregister the dpll from the subsystem, make it unavailable for
>>netlink
>>+ * API users.
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_device_free - free dpll memory
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Free memory allocated with ``dpll_device_alloc(..)``
>>+ */
>>+void dpll_device_free(struct dpll_device *dpll);
>
>
>Could you please sort the functions? I mean, dpll_device_unregister() in
>currently in the middle of dpll_device_alloc() and dpll_device_free()
>
>Also, there is no dpll_device_register(), that is odd.
>

Sure, will fix it.

>
>>+
>>+/**
>>+ * dpll_priv - get private data
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Obtain private data pointer passed to dpll subsystem when allocating
>>+ * device with ``dpll_device_alloc(..)``
>>+ */
>>+void *dpll_priv(const struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_pin_priv - get private data
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Obtain private pin data pointer passed to dpll subsystem when pin
>>+ * was registered with dpll.
>>+ */
>>+void *dpll_pin_priv(const struct dpll_device *dpll, const struct dpll_pin
>>*pin);
>>+
>>+/**
>>+ * dpll_pin_idx - get pin idx
>>+ * @dpll: pointer to dpll
>>+ * @pin: pointer to a pin
>>+ *
>>+ * Obtain pin index of given pin on given dpll.
>>+ *
>>+ * Return: PIN_IDX_INVALID - if failed to find pin, otherwise pin index
>>+ */
>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>
>You don't use this in driver (and I don't see any need for that). Remove
>from the public header.
>

Sure, will do.

>
>>+
>>+/**
>>+ * dpll_shared_pin_register - share a pin between dpll devices
>>+ * @dpll_pin_owner: a dpll already registered with a pin
>>+ * @dpll: a dpll being registered with a pin
>>+ * @shared_pin_description: identifies pin registered with dpll device
>>+ *	(@dpll_pin_owner) which is now being registered with new dpll (@dpll)
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ *
>>+ * Register a pin already registered with different dpll device.
>>+ * Allow to share a single pin within multiple dpll instances.
>>+ *
>>+ * Returns:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+int
>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>+			 struct dpll_device *dpll,
>>+			 const char *shared_pin_description,
>>+			 struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_pin_alloc - allocate memory for a new dpll_pin object
>>+ * @description: pointer to string description of a pin with max length
>>+ * equal to PIN_DESC_LEN
>>+ * @type: type of allocated pin
>>+ *
>>+ * Allocate memory for a new pin and initialize its resources.
>>+ *
>>+ * Returns:
>>+ * * pointer to initialized pin - success
>>+ * * NULL - memory allocation fail
>>+ */
>>+struct dpll_pin *dpll_pin_alloc(const char *description,
>>+				const enum dpll_pin_type type);
>>+
>>+/**
>>+ * dpll_pin_register - register pin with a dpll device
>>+ * @dpll: pointer to dpll object to register pin with
>>+ * @pin: pointer to allocated pin object being registered with dpll
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops
>>+ *
>>+ * Register previously allocated pin object with a dpll device.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this dpll,
>>+ * * -EBUSY - couldn't allocate id for a pin.
>>+ */
>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_pin_deregister - deregister pin from a dpll device
>>+ * @dpll: pointer to dpll object to deregister pin from
>>+ * @pin: pointer to allocated pin object being deregistered from dpll
>>+ *
>>+ * Deregister previously registered pin object from a dpll device.
>>+ *
>>+ * Return:
>>+ * * 0 - pin was successfully deregistered from this dpll device,
>>+ * * -ENXIO - given pin was not registered with this dpll device,
>>+ * * -EINVAL - pin pointer is not valid.
>>+ */
>>+int dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_pin_free - free memory allocated for a pin
>>+ * @pin: pointer to allocated pin object being freed
>>+ *
>>+ * Shared pins must be deregistered from all dpll devices before freeing
>>them,
>>+ * otherwise the memory won't be freed.
>>+ */
>>+void dpll_pin_free(struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_muxed_pin_register - register a pin to a muxed-type pin
>>+ * @parent_pin_description: parent pin description as given on it's
>allocation
>>+ * @pin: pointer to allocated pin object being registered with a parent
>>pin
>>+ * @ops: struct with pin ops callbacks
>>+ * @priv: private data pointer passed when calling callback ops*
>>+ *
>>+ * In case of multiplexed pins, allows registring them under a single
>>+ * parent pin.
>>+ *
>>+ * Return:
>>+ * * 0 - if pin was registered with a parent pin,
>>+ * * -ENOMEM - failed to allocate memory,
>>+ * * -EEXIST - pin already registered with this parent pin,
>>+ * * -EBUSY - couldn't assign id for a pin.
>>+ */
>>+int dpll_muxed_pin_register(struct dpll_device *dpll,
>>+			    const char *parent_pin_description,
>>+			    struct dpll_pin *pin,
>>+			    struct dpll_pin_ops *ops, void *priv);
>>+
>>+/**
>>+ * dpll_device_get_by_clock_id - find a dpll by its clock_id, type and
>>index
>>+ * @clock_id: clock_id of dpll, as given by driver on
>>``dpll_device_alloc``
>>+ * @type: type of dpll, as given by driver on ``dpll_device_alloc``
>>+ * @idx: index of dpll, as given by driver on ``dpll_device_alloc``
>>+ *
>>+ * Allows multiple driver instances using one physical DPLL to find
>>+ * and share already registered DPLL device.
>>+ *
>>+ * Return: pointer if device was found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>>+						enum dpll_type type, u8 idx);
>>+
>>+/**
>>+ * dpll_device_notify - notify on dpll device change
>>+ * @dpll: dpll device pointer
>>+ * @event: type of change
>>+ *
>>+ * Broadcast event to the netlink multicast registered listeners.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+int dpll_device_notify(struct dpll_device *dpll, enum dpll_event_change
>>event);
>>+
>>+/**
>>+ * dpll_pin_notify - notify on dpll pin change
>>+ * @dpll: dpll device pointer
>>+ * @pin: dpll pin pointer
>>+ * @event: type of change
>>+ *
>>+ * Broadcast event to the netlink multicast registered listeners.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+int dpll_pin_notify(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		    enum dpll_event_change event);
>
>You don't use this from driver, remove it from the public header.
>

Sure, makes sense.

>
>>+
>>+#endif
>>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>>new file mode 100644
>>index 000000000000..b7dbdd814b5c
>>--- /dev/null
>>+++ b/include/uapi/linux/dpll.h
>>@@ -0,0 +1,294 @@
>>+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>+#ifndef _UAPI_LINUX_DPLL_H
>>+#define _UAPI_LINUX_DPLL_H
>>+
>>+#define DPLL_NAME_LEN		32
>>+#define DPLL_DESC_LEN		20
>>+#define DPLL_PIN_DESC_LEN	20
>
>I don't see why to limit this. Those strings are read only. See
>DEVLINK_ATTR_BUS_NAME for example.
>

Sure, I think Vadim wanted to take care of it.

>
>>+
>>+/* Adding event notification support elements */
>>+#define DPLL_FAMILY_NAME	"dpll"
>>+#define DPLL_VERSION		0x01
>>+#define DPLL_MONITOR_GROUP_NAME	"monitor"
>>+
>>+#define DPLL_FILTER_PINS	1
>>+#define DPLL_FILTER_STATUS	2
>
>Why again do we need any filtering here?
>

A way to reduce output generated by dump/get requests. Assume the userspace
want to have specific information instead of everything in one packet.
They might be not needed after we introduce separated "get pin" command.

>
>>+
>>+/* dplla - Attributes of dpll generic netlink family
>>+ *
>>+ * @DPLLA_UNSPEC - invalid attribute
>>+ * @DPLLA_ID - ID of a dpll device (unsigned int)
>>+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH
>size)
>>+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
>>+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum
>dpll_mode)
>>+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll
>
>IDX
>

Sure, will fix.

>
>>+ *	(unsigned int)
>>+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
>>+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)
>
>Hmm, wouldn't it be better to have it as 1/10 of Celsius degree for
>example?
>

As we are not using it, I don't have any strong opinon on this, but seems
resonable to me, will add a divider into uAPI like:

#define DPLL_TEMP_DIVIDER	10

>
>>+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
>>+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum
>dpll_clock_class)
>>+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests
>>(int,
>>+ *	sum of DPLL_DUMP_FILTER_* defines)
>>+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
>>+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
>>+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by
>>driver
>>+ *	(char array of PIN_DESC_LEN size)
>>+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
>>+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
>>+ *	(enum dpll_pin_signal_type)
>>+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
>>+ *	(enum dpll_pin_signal_type)
>>+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for
>>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
>>+ *	(unsigned int)
>>+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
>>+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
>>+ *	(enum dpll_pin_mode)
>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>+ *	(enum dpll_change_type)
>>+ **/
>>+enum dplla {
>>+	DPLLA_UNSPEC,
>>+	DPLLA_ID,
>>+	DPLLA_NAME,
>>+	DPLLA_MODE,
>>+	DPLLA_MODE_SUPPORTED,
>>+	DPLLA_SOURCE_PIN_IDX,
>>+	DPLLA_LOCK_STATUS,
>>+	DPLLA_TEMP,
>>+	DPLLA_CLOCK_ID,
>>+	DPLLA_CLOCK_CLASS,
>>+	DPLLA_FILTER,
>>+	DPLLA_PIN,
>>+	DPLLA_PIN_IDX,
>>+	DPLLA_PIN_DESCRIPTION,
>>+	DPLLA_PIN_TYPE,
>>+	DPLLA_PIN_SIGNAL_TYPE,
>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>+	DPLLA_PIN_CUSTOM_FREQ,
>>+	DPLLA_PIN_MODE,
>>+	DPLLA_PIN_MODE_SUPPORTED,
>>+	DPLLA_PIN_PRIO,
>>+	DPLLA_PIN_PARENT_IDX,
>>+	DPLLA_PIN_NETIFINDEX,
>
>I believe we cannot have this right now. The problem is, ifindexes may
>overlay between namespaces. So ifindex without namespace means nothing.
>I don't see how this can work from the dpll side.
>

I am a bit confused, as it seemed we already had an agreement on v4 discussion
on this. And now again you highligheted it with the same reasoning as
previously. Has anything changed on that matter?

>Lets assign dpll_pin pointer to netdev and expose it over RT netlink in
>a similar way devlink_port is exposed. That should be enough for the
>user to find a dpll instance for given netdev.
>
>It does not have to be part of this set strictly, but I would like to
>have it here, so the full picture could be seen.
>

A "full picture" is getting broken if we would go with another place to keep
the reference between pin and device that produces its signal.

Why don't we add an 'struct device *' argument for dpll_pin_alloc, create
new attribute with dev_name macro similary to the current name of dpll device
name, something like: DPLLA_PIN_RCLK_DEVICE (string).
This way any device (not only netdev) would be able to add a pin and point to
a device which produces its signal.

>
>
>>+	DPLLA_CHANGE_TYPE,
>>+	__DPLLA_MAX,
>>+};
>>+
>>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>>+
>>+/* dpll_lock_status - DPLL status provides information of device status
>>+ *
>>+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
>>+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid (or
>is in
>>+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
>>+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid
>signal
>>+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
>>+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a valid
>lock
>>+ *	or was forced by DPLL_MODE_HOLDOVER mode)
>>+ **/
>>+enum dpll_lock_status {
>>+	DPLL_LOCK_STATUS_UNSPEC,
>>+	DPLL_LOCK_STATUS_UNLOCKED,
>>+	DPLL_LOCK_STATUS_CALIBRATING,
>>+	DPLL_LOCK_STATUS_LOCKED,
>>+	DPLL_LOCK_STATUS_HOLDOVER,
>>+
>>+	__DPLL_LOCK_STATUS_MAX,
>>+};
>>+
>>+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
>>+
>>+/* dpll_pin_type - signal types
>>+ *
>>+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
>>+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
>>+ * @DPLL_PIN_TYPE_EXT - external source
>>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>>+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
>>+ **/
>>+enum dpll_pin_type {
>>+	DPLL_PIN_TYPE_UNSPEC,
>>+	DPLL_PIN_TYPE_MUX,
>>+	DPLL_PIN_TYPE_EXT,
>>+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>+	DPLL_PIN_TYPE_INT_OSCILLATOR,
>>+	DPLL_PIN_TYPE_GNSS,
>>+
>>+	__DPLL_PIN_TYPE_MAX,
>>+};
>>+
>>+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
>>+
>>+/* dpll_pin_signal_type - signal types
>>+ *
>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>
>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
>with HZ value directly? For example, supported freq:
>1, 10000000
>or:
>1, 1000
>
>freq set 10000000
>freq set 1
>
>Simple and easy.
>

AFAIR, we wanted to have most commonly used frequencies as enums + custom_freq
for some exotic ones (please note that there is also possible 2PPS, which is
0.5 Hz).
This was design decision we already agreed on.
The userspace shall get definite list of comonly used frequencies that can be
used with given HW, it clearly enums are good for this.

>
>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value
>defined
>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>+ **/
>>+enum dpll_pin_signal_type {
>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>+
>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>+};
>>+
>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>+
>>+/* dpll_pin_mode - available pin states
>>+ *
>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>>+ **/
>>+enum dpll_pin_mode {
>>+	DPLL_PIN_MODE_UNSPEC,
>>+	DPLL_PIN_MODE_CONNECTED,
>>+	DPLL_PIN_MODE_DISCONNECTED,
>>+	DPLL_PIN_MODE_SOURCE,
>>+	DPLL_PIN_MODE_OUTPUT,
>
>I don't follow. I see 2 enums:
>CONNECTED/DISCONNECTED
>SOURCE/OUTPUT
>why this is mangled together? How is it supposed to be working. Like a
>bitarray?
>

The userspace shouldn't worry about bits, it recieves a list of attributes.
For current/active mode: DPLLA_PIN_MODE, and for supported modes:
DPLLA_PIN_MODE_SUPPORTED. I.e.

	DPLLA_PIN_IDX			0
	DPLLA_PIN_MODE			1,3
	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4

The reason for existance of both DPLL_PIN_MODE_CONNECTED and
DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
and bitmask is not a way to go for userspace.


>
>>+
>>+	__DPLL_PIN_MODE_MAX,
>>+};
>>+
>>+#define DPLL_PIN_MODE_MAX (__DPLL_PIN_MODE_MAX - 1)
>>+
>>+/**
>>+ * dpll_event - Events of dpll generic netlink family
>>+ *
>>+ * @DPLL_EVENT_UNSPEC - invalid event type
>>+ * @DPLL_EVENT_DEVICE_CREATE - dpll device created
>>+ * @DPLL_EVENT_DEVICE_DELETE - dpll device deleted
>>+ * @DPLL_EVENT_DEVICE_CHANGE - attribute of dpll device or pin changed
>>+ **/
>>+enum dpll_event {
>>+	DPLL_EVENT_UNSPEC,
>>+	DPLL_EVENT_DEVICE_CREATE,
>>+	DPLL_EVENT_DEVICE_DELETE,
>>+	DPLL_EVENT_DEVICE_CHANGE,
>>+
>>+	__DPLL_EVENT_MAX,
>>+};
>>+
>>+#define DPLL_EVENT_MAX (__DPLL_EVENT_MAX - 1)
>>+
>>+/**
>>+ * dpll_change_type - values of events in case of device change event
>>+ * (DPLL_EVENT_DEVICE_CHANGE)
>>+ *
>>+ * @DPLL_CHANGE_UNSPEC - invalid event type
>>+ * @DPLL_CHANGE_MODE - mode changed
>>+ * @DPLL_CHANGE_LOCK_STATUS - lock status changed
>>+ * @DPLL_CHANGE_SOURCE_PIN - source pin changed,
>
>Why comma at the end? Same to couple of others
>

It's not needed here, will remove it.

>
>>+ * @DPLL_CHANGE_TEMP - temperature changed
>>+ * @DPLL_CHANGE_PIN_ADD - source pin added,
>>+ * @DPLL_CHANGE_PIN_DEL - source pin deleted,
>>+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
>>+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
>>+ * @DPLL_CHANGE_PIN_MODE - pin state changed
>>+ * @DPLL_CHANGE_PIN_PRIO - pin prio changed
>>+ **/
>>+enum dpll_event_change {
>>+	DPLL_CHANGE_UNSPEC,
>>+	DPLL_CHANGE_MODE,
>>+	DPLL_CHANGE_LOCK_STATUS,
>>+	DPLL_CHANGE_SOURCE_PIN,
>>+	DPLL_CHANGE_TEMP,
>>+	DPLL_CHANGE_PIN_ADD,
>>+	DPLL_CHANGE_PIN_DEL,
>>+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
>>+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
>>+	DPLL_CHANGE_PIN_MODE,
>>+	DPLL_CHANGE_PIN_PRIO,
>>+
>>+	__DPLL_CHANGE_MAX,
>>+};
>>+
>>+#define DPLL_CHANGE_MAX (__DPLL_CHANGE_MAX - 1)
>>+
>>+/**
>>+ * dpll_cmd - Commands supported by the dpll generic netlink family
>>+ *
>>+ * @DPLL_CMD_UNSPEC - invalid message type
>>+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes
>of
>>+ *	single dpll device and it's pins
>>+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
>>+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
>>+ **/
>>+enum dpll_cmd {
>>+	DPLL_CMD_UNSPEC,
>>+	DPLL_CMD_DEVICE_GET,
>>+	DPLL_CMD_DEVICE_SET,
>>+	DPLL_CMD_PIN_SET,
>
>Have pin get to get list of pins, then you can have 1:1 mapping to
>events and loose the enum dpll_event_change. This is the usual way to do
>stuff. Events have the same cmd and message format as get.
>

Sure, will do.

>
>>+
>>+	__DPLL_CMD_MAX,
>>+};
>>+
>>+#define DPLL_CMD_MAX (__DPLL_CMD_MAX - 1)
>>+
>>+/**
>>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate how
>>+ * dpll selects one of its sources to syntonize with a source.
>>+ *
>>+ * @DPLL_MODE_UNSPEC - invalid
>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request
>to dpll
>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>dpll
>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>
>Why does the user care which oscilator is run internally. It's freerun,
>isn't it? If you want to expose oscilator type, you should do it
>elsewhere.
>

In NCO user might change frequency of an output, in freerun cannot.

>
>>+ **/
>>+enum dpll_mode {
>>+	DPLL_MODE_UNSPEC,
>>+	DPLL_MODE_MANUAL,
>>+	DPLL_MODE_AUTOMATIC,
>>+	DPLL_MODE_HOLDOVER,
>>+	DPLL_MODE_FREERUN,
>>+	DPLL_MODE_NCO,
>>+
>>+	__DPLL_MODE_MAX,
>>+};
>>+
>>+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
>>+
>>+/**
>>+ * dpll_clock_class - enumerate quality class of a DPLL clock as
>specified in
>>+ * Recommendation ITU-T G.8273.2/Y.1368.2.
>>+ */
>>+enum dpll_clock_class {
>>+	DPLL_CLOCK_CLASS_UNSPEC,
>>+	DPLL_CLOCK_CLASS_A,
>>+	DPLL_CLOCK_CLASS_B,
>>+	DPLL_CLOCK_CLASS_C,
>>+
>>+	__DPLL_CLOCK_CLASS_MAX,
>>+};
>>+
>>+#define DPLL_CLOCK_CLASS_MAX (__DPLL_CLOCK_CLASS_MAX - 1)
>>+
>>+/**
>>+ * enum dpll_type - type of dpll, integer value of enum is embedded into
>>+ * name of DPLL device (DPLLA_NAME)
>
>Yeah, I really cannot understand why you think for a second that
>embedding an enum value into a name makes sense in this world :O
>

Probably was to lazy to add new attribute instead :)
Will add new attribute for it.


Thanks,
Arkadiusz

>
>>+ *
>>+ * @DPLL_TYPE_UNSPEC - unspecified
>>+ * @DPLL_TYPE_PPS - dpll produces Pulse-Per-Second signal
>>+ * @DPLL_TYPE_EEC - dpll drives the Ethernet Equipment Clock
>>+ */
>>+enum dpll_type {
>>+	DPLL_TYPE_UNSPEC,
>>+	DPLL_TYPE_PPS,
>>+	DPLL_TYPE_EEC,
>>+
>>+	__DPLL_TYPE_MAX
>>+};
>>+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
>>+
>>+#endif /* _UAPI_LINUX_DPLL_H */
>>--
>>2.30.2
>>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* RE: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-01-20 12:56       ` Jiri Pirko
@ 2023-01-27 18:12         ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-27 18:12 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, January 20, 2023 1:57 PM
>
>Thu, Jan 19, 2023 at 06:16:13PM CET, jiri@resnulli.us wrote:
>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>>>+/**
>>>+ * dpll_cmd - Commands supported by the dpll generic netlink family
>>>+ *
>>>+ * @DPLL_CMD_UNSPEC - invalid message type
>>>+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes
>>of
>>>+ *	single dpll device and it's pins
>>>+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
>>>+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
>>>+ **/
>>>+enum dpll_cmd {
>>>+	DPLL_CMD_UNSPEC,
>>>+	DPLL_CMD_DEVICE_GET,
>>>+	DPLL_CMD_DEVICE_SET,
>>>+	DPLL_CMD_PIN_SET,
>>
>>Have pin get to get list of pins, then you can have 1:1 mapping to
>>events and loose the enum dpll_event_change. This is the usual way to do
>>stuff. Events have the same cmd and message format as get.
>
>I was thinking about that a bit more.
>1) There is 1:n relationship between PIN and DPLL(s).
>2) The pin configuration is independent on DPLL, with an
>   exeption of PRIO.
>

DPLL configuration is dependant on PIN configuration.
If the pin is configured with a frequency/mode all of dplls connected with it
shall know about it.

>Therefore as I suggested in the reply to this patch, the pin should be
>separate entity, allocated and having ops unrelated to DPLL. It is just
>registered to the DPLLs that are using the pin.
>

Don't mind having them as separated entities (as they are already separated in
kernel space, allocated separately and registered with dpll/s as needed).
You might also remember that first version had the "global" list of pins, which
was removed due to the comments. For having get/dump pins command it would be
better to fallback to that approach, with global XA of pins in dpll_core.c.

>The pin ops should not have dpll pointer as arg, again with exception of
>PRIO.
>

Sure, probably with this approach we could remove dpll pointer for global-pin
attributes.

>DPLL_CMD_DEVICE_GET should not contain pins at all.
>
>There should be DPLL_CMD_PIN_GET added which can dump and will be used
>to get the list of pins in the system.
>- if DPLL handle is passed to DPLL_CMD_PIN_GET, it will dump only pins
>  related to the specified DPLL.
>

Sure, we agreed on that already.

>DPLL_CMD_PIN_GET message will contain pin-specific attrs and will have a
>list of connected DPLLs:
>       DPLLA_PIN_IDX
>       DPLLA_PIN_DESCRIPTION
>       DPLLA_PIN_TYPE
>       DPLLA_PIN_SIGNAL_TYPE
>       DPLLA_PIN_SIGNAL_TYPE_SUPPORTED
>       DPLLA_PIN_CUSTOM_FREQ
>       DPLLA_PIN_MODE
>       DPLLA_PIN_MODE_SUPPORTED
>       DPLLA_PIN_PARENT_IDX
>       DPLLA_PIN_DPLL    (nested)
>          DPLLA_DPLL_HANDLE   "dpll_0"
>          DPLLA_PIN_PRIO    1
>       DPLLA_PIN_DPLL    (nested)
>          DPLLA_DPLL_HANDLE   "dpll_1"
>          DPLLA_PIN_PRIO    2
>
>Please take the names lightly. My point is to show 2 nests for 2
>DPLLS connected, on each the pin has different prio.
>
>Does this make sense?
>

Seems good, I guess dpll referenced by giving their bus/name, as below.

>One issue to be solved is the pin indexing. As pin would be separate
>entity, the indexing would be global and therefore not predictable. We
>would have to figure it out differntly. Pehaps something like this:
>
>$ dpll dev show
>pci/0000:08:00.0: dpll 1             first dpll on 0000:08:00.0
>pci/0000:08:00.0: dpll 2             second dpll on the same pci device
>pci/0000:09:00.0: dpll 1             first dpll on 0000:09:00.0
>pci/0000:09:00.0: dpll 2             second dpll on the same pci device
>
>$ dpll pin show
>pci/0000:08:00.0: pin 1 desc SOMELABEL_A
>  dpll 1:                          This refers to DPLL 1 on the same pci
>device
>    prio 80
>  dpll 2:                          This refers to DPLL 2 on the same pci
>device
>    prio 100
>pci/0000:08:00.0: pin 2 desc SOMELABEL_B
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:08:00.0: pin 3 desc SOMELABEL_C
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:08:00.0: pin 4 desc SOMELABEL_D
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:09:00.0: pin 1 desc SOMEOTHERLABEL_A
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:09:00.0: pin 2 desc SOMEOTHERLABEL_B
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:09:00.0: pin 3 desc SOMEOTHERLABEL_C
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:09:00.0: pin 4 desc SOMEOTHERLABEL_C
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>
>Note there are 2 groups of pins, one for each pci device.
>
>Setting some attribute command would looks like:
>To set DPLL mode:
>$ dpll dev set pci/0000:08:00.0 dpll 1 mode freerun
>   netlink:
>   DPLL_CMD_DEVICE_SET
>      DPLLA_BUS_NAME "pci"
>      DPLLA_DEV_NAME "0000:08:00.0"
>      DPLLA_DPLL_INDEX 1
>      DPLLA_DPLL_MODE 3
>
>$ dpll dev set pci/0000:08:00.0 dpll 2 mode automatic
>
>
>To set signal frequency in HZ:
>$ dpll pin set pci/0000:08:00.0 pin 3 frequency 10000000
>   netlink:
>   DPLL_CMD_PIN_SET
>      DPLLA_BUS_NAME "pci"
>      DPLLA_DEV_NAME "0000:08:00.0"
>      DPLLA_PIN_INDEX 3
>      DPLLA_PIN_FREQUENCY 10000000
>
>$ dpll pin set pci/0000:08:00.0 pin 1 frequency 1
>
>
>To set individual of one pin for 2 DPLLs:
>$ dpll pin set pci/0000:08:00.0 pin 1 dpll 1 prio 40
>   netlink:
>   DPLL_CMD_PIN_SET
>      DPLLA_BUS_NAME "pci"
>      DPLLA_DEV_NAME "0000:08:00.0"
>      DPLLA_PIN_INDEX 1
>      DPLLA_DPLL_INDEX 1
>      DPLLA_PIN_PRIO 40
>
>$ dpll pin set pci/0000:08:00.0 pin 1 dpll 2 prio 80
>
>
>Isn't this neat?
>
>
>[...]

Seems so.

As I suggested in other response on this thread, the best solution for
pin index, would be IMHO to allow registering driver assign index to a pin.
This would also solve/simplify sharing the pins. As the other driver would
just pass that index to register it with second dpll.

Also all dplls shall have the possibility to be notified that a pin which was
registered with them has changed (except prio which is set only for single
dpll, and for prio only one callback shall be invoked).
This is because not all dplls which are sharing pins will be controlled by the
same FW/driver instance.
Currently for registering a pin with second dpll, the caller passes another
set of callback ops, which is then held in dpll_pin_ref - for all dplls which
were registered with the pin. But as this is not always needed (i.e. same
instance controlling all dplls and pins), thus kernel module can pass ops=NULL
when registering a pin with second dpll, efectively executing only one callback
per pin.

Will try to have it done in next version.

Thanks,
Arkadiusz

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

* RE: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-01-27 18:12         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-27 18:12 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, January 20, 2023 1:57 PM
>
>Thu, Jan 19, 2023 at 06:16:13PM CET, jiri@resnulli.us wrote:
>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>>>+/**
>>>+ * dpll_cmd - Commands supported by the dpll generic netlink family
>>>+ *
>>>+ * @DPLL_CMD_UNSPEC - invalid message type
>>>+ * @DPLL_CMD_DEVICE_GET - Get list of dpll devices (dump) or attributes
>>of
>>>+ *	single dpll device and it's pins
>>>+ * @DPLL_CMD_DEVICE_SET - Set attributes for a dpll
>>>+ * @DPLL_CMD_PIN_SET - Set attributes for a pin
>>>+ **/
>>>+enum dpll_cmd {
>>>+	DPLL_CMD_UNSPEC,
>>>+	DPLL_CMD_DEVICE_GET,
>>>+	DPLL_CMD_DEVICE_SET,
>>>+	DPLL_CMD_PIN_SET,
>>
>>Have pin get to get list of pins, then you can have 1:1 mapping to
>>events and loose the enum dpll_event_change. This is the usual way to do
>>stuff. Events have the same cmd and message format as get.
>
>I was thinking about that a bit more.
>1) There is 1:n relationship between PIN and DPLL(s).
>2) The pin configuration is independent on DPLL, with an
>   exeption of PRIO.
>

DPLL configuration is dependant on PIN configuration.
If the pin is configured with a frequency/mode all of dplls connected with it
shall know about it.

>Therefore as I suggested in the reply to this patch, the pin should be
>separate entity, allocated and having ops unrelated to DPLL. It is just
>registered to the DPLLs that are using the pin.
>

Don't mind having them as separated entities (as they are already separated in
kernel space, allocated separately and registered with dpll/s as needed).
You might also remember that first version had the "global" list of pins, which
was removed due to the comments. For having get/dump pins command it would be
better to fallback to that approach, with global XA of pins in dpll_core.c.

>The pin ops should not have dpll pointer as arg, again with exception of
>PRIO.
>

Sure, probably with this approach we could remove dpll pointer for global-pin
attributes.

>DPLL_CMD_DEVICE_GET should not contain pins at all.
>
>There should be DPLL_CMD_PIN_GET added which can dump and will be used
>to get the list of pins in the system.
>- if DPLL handle is passed to DPLL_CMD_PIN_GET, it will dump only pins
>  related to the specified DPLL.
>

Sure, we agreed on that already.

>DPLL_CMD_PIN_GET message will contain pin-specific attrs and will have a
>list of connected DPLLs:
>       DPLLA_PIN_IDX
>       DPLLA_PIN_DESCRIPTION
>       DPLLA_PIN_TYPE
>       DPLLA_PIN_SIGNAL_TYPE
>       DPLLA_PIN_SIGNAL_TYPE_SUPPORTED
>       DPLLA_PIN_CUSTOM_FREQ
>       DPLLA_PIN_MODE
>       DPLLA_PIN_MODE_SUPPORTED
>       DPLLA_PIN_PARENT_IDX
>       DPLLA_PIN_DPLL    (nested)
>          DPLLA_DPLL_HANDLE   "dpll_0"
>          DPLLA_PIN_PRIO    1
>       DPLLA_PIN_DPLL    (nested)
>          DPLLA_DPLL_HANDLE   "dpll_1"
>          DPLLA_PIN_PRIO    2
>
>Please take the names lightly. My point is to show 2 nests for 2
>DPLLS connected, on each the pin has different prio.
>
>Does this make sense?
>

Seems good, I guess dpll referenced by giving their bus/name, as below.

>One issue to be solved is the pin indexing. As pin would be separate
>entity, the indexing would be global and therefore not predictable. We
>would have to figure it out differntly. Pehaps something like this:
>
>$ dpll dev show
>pci/0000:08:00.0: dpll 1             first dpll on 0000:08:00.0
>pci/0000:08:00.0: dpll 2             second dpll on the same pci device
>pci/0000:09:00.0: dpll 1             first dpll on 0000:09:00.0
>pci/0000:09:00.0: dpll 2             second dpll on the same pci device
>
>$ dpll pin show
>pci/0000:08:00.0: pin 1 desc SOMELABEL_A
>  dpll 1:                          This refers to DPLL 1 on the same pci
>device
>    prio 80
>  dpll 2:                          This refers to DPLL 2 on the same pci
>device
>    prio 100
>pci/0000:08:00.0: pin 2 desc SOMELABEL_B
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:08:00.0: pin 3 desc SOMELABEL_C
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:08:00.0: pin 4 desc SOMELABEL_D
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:09:00.0: pin 1 desc SOMEOTHERLABEL_A
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:09:00.0: pin 2 desc SOMEOTHERLABEL_B
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:09:00.0: pin 3 desc SOMEOTHERLABEL_C
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>pci/0000:09:00.0: pin 4 desc SOMEOTHERLABEL_C
>  dpll 1:
>    prio 80
>  dpll 2:
>    prio 100
>
>Note there are 2 groups of pins, one for each pci device.
>
>Setting some attribute command would looks like:
>To set DPLL mode:
>$ dpll dev set pci/0000:08:00.0 dpll 1 mode freerun
>   netlink:
>   DPLL_CMD_DEVICE_SET
>      DPLLA_BUS_NAME "pci"
>      DPLLA_DEV_NAME "0000:08:00.0"
>      DPLLA_DPLL_INDEX 1
>      DPLLA_DPLL_MODE 3
>
>$ dpll dev set pci/0000:08:00.0 dpll 2 mode automatic
>
>
>To set signal frequency in HZ:
>$ dpll pin set pci/0000:08:00.0 pin 3 frequency 10000000
>   netlink:
>   DPLL_CMD_PIN_SET
>      DPLLA_BUS_NAME "pci"
>      DPLLA_DEV_NAME "0000:08:00.0"
>      DPLLA_PIN_INDEX 3
>      DPLLA_PIN_FREQUENCY 10000000
>
>$ dpll pin set pci/0000:08:00.0 pin 1 frequency 1
>
>
>To set individual of one pin for 2 DPLLs:
>$ dpll pin set pci/0000:08:00.0 pin 1 dpll 1 prio 40
>   netlink:
>   DPLL_CMD_PIN_SET
>      DPLLA_BUS_NAME "pci"
>      DPLLA_DEV_NAME "0000:08:00.0"
>      DPLLA_PIN_INDEX 1
>      DPLLA_DPLL_INDEX 1
>      DPLLA_PIN_PRIO 40
>
>$ dpll pin set pci/0000:08:00.0 pin 1 dpll 2 prio 80
>
>
>Isn't this neat?
>
>
>[...]

Seems so.

As I suggested in other response on this thread, the best solution for
pin index, would be IMHO to allow registering driver assign index to a pin.
This would also solve/simplify sharing the pins. As the other driver would
just pass that index to register it with second dpll.

Also all dplls shall have the possibility to be notified that a pin which was
registered with them has changed (except prio which is set only for single
dpll, and for prio only one callback shall be invoked).
This is because not all dplls which are sharing pins will be controlled by the
same FW/driver instance.
Currently for registering a pin with second dpll, the caller passes another
set of callback ops, which is then held in dpll_pin_ref - for all dplls which
were registered with the pin. But as this is not always needed (i.e. same
instance controlling all dplls and pins), thus kernel module can pass ops=NULL
when registering a pin with second dpll, efectively executing only one callback
per pin.

Will try to have it done in next version.

Thanks,
Arkadiusz

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* RE: [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
  2023-01-19 14:54     ` Jiri Pirko
@ 2023-01-27 18:13       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-27 18:13 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal


>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, January 19, 2023 3:54 PM
>
>Tue, Jan 17, 2023 at 07:00:51PM CET, vadfed@meta.com wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>Control over clock generation unit is required for further development
>>of Synchronous Ethernet feature. Interface provides ability to obtain
>>current state of a dpll, its sources and outputs which are pins, and
>>allows their configuration.
>>
>>Co-developed-by: Milena Olech <milena.olech@intel.com>
>>Signed-off-by: Milena Olech <milena.olech@intel.com>
>>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
>>---
>> drivers/net/ethernet/intel/Kconfig        |    1 +
>> drivers/net/ethernet/intel/ice/Makefile   |    3 +-
>> drivers/net/ethernet/intel/ice/ice.h      |    4 +
>> drivers/net/ethernet/intel/ice/ice_dpll.c | 2115 +++++++++++++++++++++
>> drivers/net/ethernet/intel/ice/ice_dpll.h |   99 +
>> drivers/net/ethernet/intel/ice/ice_main.c |   10 +
>> 6 files changed, 2231 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
>> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
>>
>>diff --git a/drivers/net/ethernet/intel/Kconfig
>>b/drivers/net/ethernet/intel/Kconfig
>>index 3facb55b7161..dcf0e12991bf 100644
>>--- a/drivers/net/ethernet/intel/Kconfig
>>+++ b/drivers/net/ethernet/intel/Kconfig
>>@@ -300,6 +300,7 @@ config ICE
>> 	select DIMLIB
>> 	select NET_DEVLINK
>> 	select PLDMFW
>>+	select DPLL
>> 	help
>> 	  This driver supports Intel(R) Ethernet Connection E800 Series of
>> 	  devices.  For more information on how to identify your adapter, go
>>diff --git a/drivers/net/ethernet/intel/ice/Makefile
>>b/drivers/net/ethernet/intel/ice/Makefile
>>index 9183d480b70b..ebfd456f76cd 100644
>>--- a/drivers/net/ethernet/intel/ice/Makefile
>>+++ b/drivers/net/ethernet/intel/ice/Makefile
>>@@ -32,7 +32,8 @@ ice-y := ice_main.o	\
>> 	 ice_lag.o	\
>> 	 ice_ethtool.o  \
>> 	 ice_repr.o	\
>>-	 ice_tc_lib.o
>>+	 ice_tc_lib.o	\
>>+	 ice_dpll.o
>> ice-$(CONFIG_PCI_IOV) +=	\
>> 	ice_sriov.o		\
>> 	ice_virtchnl.o		\
>>diff --git a/drivers/net/ethernet/intel/ice/ice.h
>>b/drivers/net/ethernet/intel/ice/ice.h
>>index 3368dba42789..efc1844ef77c 100644
>>--- a/drivers/net/ethernet/intel/ice/ice.h
>>+++ b/drivers/net/ethernet/intel/ice/ice.h
>>@@ -73,6 +73,7 @@
>> #include "ice_lag.h"
>> #include "ice_vsi_vlan_ops.h"
>> #include "ice_gnss.h"
>>+#include "ice_dpll.h"
>>
>> #define ICE_BAR0		0
>> #define ICE_REQ_DESC_MULTIPLE	32
>>@@ -198,6 +199,7 @@
>> enum ice_feature {
>> 	ICE_F_DSCP,
>> 	ICE_F_PTP_EXTTS,
>>+	ICE_F_PHY_RCLK,
>> 	ICE_F_SMA_CTRL,
>> 	ICE_F_CGU,
>> 	ICE_F_GNSS,
>>@@ -509,6 +511,7 @@ enum ice_pf_flags {
>> 	ICE_FLAG_PLUG_AUX_DEV,
>> 	ICE_FLAG_MTU_CHANGED,
>> 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
>>+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
>> 	ICE_PF_FLAGS_NBITS		/* must be last */
>> };
>>
>>@@ -633,6 +636,7 @@ struct ice_pf {
>> #define ICE_VF_AGG_NODE_ID_START	65
>> #define ICE_MAX_VF_AGG_NODES		32
>> 	struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
>>+	struct ice_dplls dplls;
>> };
>>
>> struct ice_netdev_priv {
>>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c
>>b/drivers/net/ethernet/intel/ice/ice_dpll.c
>>new file mode 100644
>>index 000000000000..6ed1fcee4e03
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>>@@ -0,0 +1,2115 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/* Copyright (C) 2022, Intel Corporation. */
>>+
>>+#include "ice.h"
>>+#include "ice_lib.h"
>>+#include "ice_trace.h"
>>+#include <linux/dpll.h>
>>+#include <uapi/linux/dpll.h>
>
>Don't include uapi header directly. Inlude of linux/dpll.h is enough.
>

True, will fix.

>
>>+
>>+#define CGU_STATE_ACQ_ERR_THRESHOLD	50
>>+#define ICE_DPLL_FREQ_1_HZ		1
>>+#define ICE_DPLL_FREQ_10_MHZ		10000000
>>+
>>+/**
>>+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock
>>status
>>+ */
>>+static const enum dpll_lock_status
>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>
>Don't use __ define.
>

Sure, will fix.

>
>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>+};
>>+
>>+/**
>>+ * ice_dpll_pin_type - enumerate ince pin types
>>+ */
>>+enum ice_dpll_pin_type {
>>+	ICE_DPLL_PIN_INVALID = 0,
>>+	ICE_DPLL_PIN_TYPE_SOURCE,
>>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>>+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
>>+};
>>+
>>+/**
>>+ * pin_type_name - string names of ice pin types
>>+ */
>>+static const char * const pin_type_name[] = {
>>+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
>>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>>+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
>>+};
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_to_freq - translate pin_signal_type to freq
>>value
>>+ * @sig_type: signal type to find frequency
>>+ * @freq: on success - frequency of a signal type
>>+ *
>>+ * Return:
>>+ * * 0 - frequency valid
>>+ * * negative - error
>>+ */
>>+static inline int
>>+ice_dpll_pin_signal_type_to_freq(const enum dpll_pin_signal_type
>>sig_type,
>>+				 u32 *freq)
>>+{
>>+	if (sig_type == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>>+		return -EINVAL;
>>+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_1_PPS)
>>+		*freq = ICE_DPLL_FREQ_1_HZ;
>>+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_10_MHZ)
>>+		*freq = ICE_DPLL_FREQ_10_MHZ;
>>+	else
>>+		return -EINVAL;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_freq_to_signal_type - translate pin freq to signal type
>>+ * @freq: frequency to translate
>>+ *
>>+ * Return: signal type of a pin based on frequency.
>>+ */
>>+static inline enum dpll_pin_signal_type
>>+ice_dpll_pin_freq_to_signal_type(u32 freq)
>>+{
>>+	if (freq == ICE_DPLL_FREQ_1_HZ)
>>+		return DPLL_PIN_SIGNAL_TYPE_1_PPS;
>>+	else if (freq == ICE_DPLL_FREQ_10_MHZ)
>>+		return DPLL_PIN_SIGNAL_TYPE_10_MHZ;
>>+	else
>>+		return DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_set - set pin's signal type in hardware
>>+ * @pf: Board private structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being configured
>>+ * @sig_type: signal type to be set
>>+ *
>>+ * Translate pin signal type to frequency and set it on a pin.
>>+ *
>>+ * Return:
>>+ * * 0 - OK or no change required
>>+ * * negative - error
>>+ */
>>+static int
>>+__ice_dpll_pin_signal_type_set(struct ice_pf *pf, struct ice_dpll_pin
>>*pin,
>>+			       const enum ice_dpll_pin_type pin_type,
>>+			       const enum dpll_pin_signal_type sig_type)
>>+{
>>+	u8 flags;
>>+	u32 freq;
>>+	int ret;
>>+
>>+	ret = ice_dpll_pin_signal_type_to_freq(sig_type, &freq);
>>+	if (ret)
>>+		return ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>>+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>>+					       pin->flags, freq, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags = pin->flags | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>>+						0, freq, 0);
>>+	} else {
>>+		ret = -EINVAL;
>>+	}
>>+
>>+	if (ret) {
>>+		dev_dbg(ice_pf_to_dev(pf),
>>+			"err:%d %s failed to set pin freq:%u on pin:%u\n",
>>+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>>+			freq, pin->idx);
>>+	} else {
>>+		pin->signal_type = sig_type;
>>+		pin->freq = freq;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_enable - enable a pin on dplls
>>+ * @hw: board private hw structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being enabled
>>+ *
>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		    const enum ice_dpll_pin_type pin_type)
>>+{
>>+	u8 flags = pin->flags;
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
>>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		flags |= ICE_DPLL_RCLK_SOURCE_FLAG_EN;
>>+		ret = 0;
>>+	}
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+			"err:%d %s failed to enable %s pin:%u\n",
>>+			ret, ice_aq_str(hw->adminq.sq_last_status),
>>+			pin_type_name[pin_type], pin->idx);
>>+	else
>>+		pin->flags = flags;
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_disable - disable a pin on dplls
>>+ * @hw: board private hw structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being disabled
>>+ *
>>+ * Disable a pin on both dplls. Store current state in pin->flags.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		     enum ice_dpll_pin_type pin_type)
>>+{
>>+	u8 flags = pin->flags;
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
>>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
>>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		flags &= ~(ICE_DPLL_RCLK_SOURCE_FLAG_EN);
>>+		ret = ice_aq_set_phy_rec_clk_out(hw, pin->idx, false,
>>+						 &pin->freq);
>>+	}
>>+
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+			"err:%d %s failed to disable %s pin:%u\n",
>>+			ret, ice_aq_str(hw->adminq.sq_last_status),
>>+			pin_type_name[pin_type], pin->idx);
>>+	else
>>+		pin->flags = flags;
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_update - update pin's mode
>>+ * @hw: private board struct
>>+ * @pin: structure with pin attributes to be updated
>>+ * @pin_type: type of pin being updated
>>+ *
>>+ * Determine pin current mode, frequency and signal type. Then update
>>struct
>>+ * holding the pin info.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+int
>>+ice_dpll_pin_update(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		    const enum ice_dpll_pin_type pin_type)
>>+{
>>+	int ret;
>>+
>>+	pin->mode_mask = 0;
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		ret = ice_aq_get_input_pin_cfg(hw, pin->idx, NULL, NULL, NULL,
>>+					       &pin->flags, &pin->freq, NULL);
>>+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
>>+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags)
>>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>>+		else
>>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		ret = ice_aq_get_output_pin_cfg(hw, pin->idx, &pin->flags,
>>+						NULL, &pin->freq, NULL);
>>+		set_bit(DPLL_PIN_MODE_OUTPUT, &pin->mode_mask);
>>+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags)
>>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>>+		else
>>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
>>+		if (ICE_DPLL_RCLK_SOURCE_FLAG_EN & pin->flags)
>>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>>+		else
>>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>>+		ret = 0;
>>+	}
>>+	pin->signal_type = ice_dpll_pin_freq_to_signal_type(pin->freq);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_mode_set - set pin's mode
>>+ * @pf: Board private structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of modified pin
>>+ * @mode: requested mode
>>+ *
>>+ * Determine requested pin mode set it on a pin.
>>+ *
>>+ * Return:
>>+ * * 0 - OK or no change required
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_mode_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+		      const enum ice_dpll_pin_type pin_type,
>>+		      const enum dpll_pin_mode mode)
>>+{
>>+	int ret;
>>+
>>+	if (!test_bit(mode, &pin->mode_supported_mask))
>>+		return -EINVAL;
>>+
>>+	if (test_bit(mode, &pin->mode_mask))
>>+		return 0;
>>+
>>+	if (mode == DPLL_PIN_MODE_CONNECTED)
>>+		ret = ice_dpll_pin_enable(&pf->hw, pin, pin_type);
>>+	else if (mode == DPLL_PIN_MODE_DISCONNECTED)
>>+		ret = ice_dpll_pin_disable(&pf->hw, pin, pin_type);
>>+	else
>>+		ret = -EINVAL;
>>+
>>+	if (!ret)
>>+		ret = ice_dpll_pin_update(&pf->hw, pin, pin_type);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_find_dpll - find ice_dpll on a pf
>>+ * @pf: private board structure
>>+ * @dpll: kernel's dpll_device pointer to be searched
>>+ *
>>+ * Return:
>>+ * * pointer if ice_dpll with given device dpll pointer is found
>>+ * * NULL if not found
>>+ */
>>+static struct ice_dpll
>>+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
>>+{
>>+	if (!pf || !dpll)
>>+		return NULL;
>>+
>>+	return (dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
>>+		dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL);
>
>Return is not a function, don't use ()'s here.

Sure, will fix.

>
>
>>+}
>>+
>>+/**
>>+ * ice_find_pin - find ice_dpll_pin on a pf
>>+ * @pf: private board structure
>>+ * @pin: kernel's dpll_pin pointer to be searched for
>>+ * @pin_type: type of pins to be searched for
>>+ *
>>+ * Find and return internal ice pin info pointer holding data of given
>>dpll subsystem
>>+ * pin pointer.
>>+ *
>>+ * Return:
>>+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer
>>was
>>+ * found in pf internal pin data.
>>+ * * NULL - if pin was not found.
>>+ */
>>+static struct ice_dpll_pin
>>+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
>>+	      enum ice_dpll_pin_type pin_type)
>>+
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	int pin_num, i;
>>+
>>+	if (!pin || !pf)
>>+		return NULL;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		pins = pf->dplls.inputs;
>>+		pin_num = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		pins = pf->dplls.outputs;
>>+		pin_num = pf->dplls.num_outputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		pins = pf->dplls.rclk;
>>+		pin_num = pf->dplls.num_rclk;
>>+	} else {
>>+		return NULL;
>>+	}
>>+
>>+	for (i = 0; i < pin_num; i++)
>>+		if (pin == pins[i].pin)
>>+			return &pins[i];
>>+
>>+	return NULL;
>>+}
>>+
>>+/**
>>+ * ice_dpll_hw_source_prio_set - set source priority value in hardware
>>+ * @pf: board private structure
>>+ * @dpll: ice dpll pointer
>>+ * @pin: ice pin pointer
>>+ * @prio: priority value being set on a dpll
>>+ *
>>+ * Internal wrapper for setting the priority in the hardware.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_hw_source_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
>>+			    struct ice_dpll_pin *pin, const u32 prio)
>>+{
>>+	int ret;
>>+
>>+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
>>+				      (u8)prio);
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev(pf),
>>+			"err:%d %s failed to set pin prio:%u on pin:%u\n",
>>+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>>+			prio, pin->idx);
>>+	else
>>+		dpll->input_prio[pin->idx] = prio;
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_lock_status_get - get dpll lock status callback
>>+ * @dpll: registered dpll pointer
>>+ * @status: on success holds dpll's lock status
>>+ *
>>+ * Dpll subsystem callback, provides dpll's lock status.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_lock_status_get(const struct dpll_device *dpll,
>>+				    enum dpll_lock_status *status)
>>+{
>>+	struct ice_pf *pf = dpll_priv(dpll);
>>+	struct ice_dpll *d;
>>+
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		return -EFAULT;
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll,
>>pf);
>>+	mutex_lock(&pf->dplls.lock);
>>+	*status = ice_dpll_status[d->dpll_state];
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_idx_get - get dpll's source index
>>+ * @dpll: registered dpll pointer
>>+ * @pin_idx: on success holds currently selected source pin index
>>+ *
>>+ * Dpll subsystem callback. Provides index of a source dpll is trying to
>>lock
>>+ * with.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_idx_get(const struct dpll_device *dpll, u32
>>*pin_idx)
>
>Should return struct dpll_pin *. That should be the consitent driver api
>handle for pin.
>

Sure, can fix this way.

>
>>+{
>>+	struct ice_pf *pf = dpll_priv(dpll);
>>+	struct ice_dpll *d;
>>+	int ret = 0;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	*pin_idx = (u32)d->source_idx;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p d:%p, idx:%u\n",
>>+		__func__, dpll, pf, d, *pin_idx);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_mode_get - get dpll's working mode
>>+ * @dpll: registered dpll pointer
>>+ * @mode: on success holds current working mode of dpll
>>+ *
>>+ * Dpll subsystem callback. Provides working mode of dpll.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_mode_get(const struct dpll_device *dpll,
>>+			     enum dpll_mode *mode)
>>+{
>>+	struct ice_pf *pf = dpll_priv(dpll);
>>+	struct ice_dpll *d;
>>+	int ret = 0;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		ret = -EFAULT;
>>+	else
>>+		*mode = DPLL_MODE_AUTOMATIC;
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_mode_get - check if dpll's working mode is supported
>>+ * @dpll: registered dpll pointer
>>+ * @mode: mode to be checked for support
>>+ *
>>+ * Dpll subsystem callback. Provides information if working mode is
>>supported
>>+ * by dpll.
>>+ *
>>+ * Return:
>>+ * * true - mode is supported
>>+ * * false - mode is not supported
>>+ */
>>+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
>>+				    const enum dpll_mode mode)
>>+{
>>+	struct ice_pf *pf = dpll_priv(dpll);
>>+	struct ice_dpll *d;
>>+	bool ret = true;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		ret = false;
>>+	else
>>+		if (mode != DPLL_MODE_AUTOMATIC)
>
>"else if" on a single line.

Sure, will fix.

>
>
>>+			ret = false;
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_supported - if pin signal type is supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type being checked for support
>>+ * @pin_type: type of a pin being checked
>>+ *
>>+ * Check is signal type is supported on given pin/dpll pair.
>>+ *
>>+ * Return:
>>+ * * true - supported
>>+ * * false - not supported
>>+ */
>>+static bool
>>+ice_dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>>+				   const struct dpll_pin *pin,
>>+				   const enum dpll_pin_signal_type sig_type,
>>+				   const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	bool supported = false;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (p) {
>>+		if (test_bit(sig_type, &p->signal_type_mask))
>
>Loose the nested if.

Sure, will fix.

>
>
>>+			supported = true;
>>+	}
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return supported;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_signal_type_supported - if rclk source signal type is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type being checked for support
>>+ *
>>+ * Dpll subsystem callback. Check is signal type is supported on given
>>pin/dpll
>>+ * pair.
>>+ *
>>+ * Return:
>>+ * * true - supported
>>+ * * false - not supported
>>+ */
>>+static bool
>>+ice_dpll_rclk_signal_type_supported(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin,
>>+				    const enum dpll_pin_signal_type sig_type)
>>+{
>>+	const enum ice_dpll_pin_type t = ICE_DPLL_PIN_TYPE_RCLK_SOURCE;
>>+
>>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type, t);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_signal_type_supported - if source signal type is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type being checked for support
>>+ *
>>+ * Dpll subsystem callback. Check is signal type is supported on given
>>pin/dpll
>>+ * pair.
>>+ *
>>+ * Return:
>>+ * * true - supported
>>+ * * false - not supported
>>+ */
>>+static bool
>>+ice_dpll_source_signal_type_supported(const struct dpll_device *dpll,
>>+				      const struct dpll_pin *pin,
>>+				      const enum dpll_pin_signal_type sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
>>+						  ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_signal_type_supported - if output pin signal type is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type being checked
>>+ *
>>+ * Dpll subsystem callback. Check if signal type is supported on given
>>in/dpll
>>+ * pair.
>>+ *
>>+ * Return:
>>+ * * true - supported
>>+ * * false - not supported
>>+ */
>>+static bool
>>+ice_dpll_output_signal_type_supported(const struct dpll_device *dpll,
>>+				      const struct dpll_pin *pin,
>>+				      const enum dpll_pin_signal_type sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
>>+						  ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_get - get dpll's pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: on success - current signal type used on the pin
>>+ * @pin_type: type of a pin being checked
>>+ *
>>+ * Find a pin and assign sig_type with its current signal type value.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_pin_signal_type_get(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					enum dpll_pin_signal_type *sig_type,
>>+					const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	int ret = -ENODEV;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (p) {
>>+		*sig_type = p->signal_type;
>>+		ret = 0;
>>+	}
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p\n",
>>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_signal_type_get - get dpll's output pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: on success - current signal type value used on the pin
>>+ *
>>+ * Dpll subsystem callback. Find current signal type of given pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_output_signal_type_get(const struct dpll_device
>>*dpll,
>>+					   const struct dpll_pin *pin,
>>+					   enum dpll_pin_signal_type *sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
>>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_set - set dpll pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type to be set
>>+ * @pin_type: type of a pin being configured
>>+ *
>>+ * Handler for signal type modification on pins. Set signal type value
>>for
>>+ * a given pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_pin_signal_type_set(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					const enum dpll_pin_signal_type sig_type,
>>+					const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EFAULT;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p)
>>+		goto unlock;
>>+	if (test_bit(sig_type, &p->signal_type_mask))
>>+		ret = __ice_dpll_pin_signal_type_set(pf, p, pin_type,
>>sig_type);
>>+	else
>>+		ret = -EINVAL;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
>>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL,  ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_signal_type_set - set dpll output pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type to be set
>>+ *
>>+ * Dpll subsystem callback. Wraps signal type modification handler.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_output_signal_type_set(const struct dpll_device *dpll,
>>+				const struct dpll_pin *pin,
>>+				const enum dpll_pin_signal_type sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
>>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_mode_enable - enables a pin mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be set
>>+ * @pin_type: type of pin being modified
>>+ *
>>+ * Handler for enabling the pin mode.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_pin_mode_enable(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin,
>>+				    const enum dpll_pin_mode mode,
>>+				    const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EFAULT;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (p)
>>+		ret = ice_dpll_pin_mode_set(pf, p, pin_type, mode);
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
>>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_mode_enable - enable rclk-source pin mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be set
>>+ *
>>+ * Dpll subsystem callback. Enables given mode on recovered clock source
>>type
>>+ * pin.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_rclk_mode_enable(const struct dpll_device *dpll,
>>+				     const struct dpll_pin *pin,
>>+				     const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_mode_enable - enable output pin mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be set
>>+ *
>>+ * Dpll subsystem callback. Enables given mode on output type pin.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_output_mode_enable(const struct dpll_device *dpll,
>>+				       const struct dpll_pin *pin,
>>+				       const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>>+					 ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_mode_enable - enable source pin mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be set
>>+ *
>>+ * Dpll subsystem callback. Enables given mode on source type pin.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_source_mode_enable(const struct dpll_device *dpll,
>>+				       const struct dpll_pin *pin,
>>+				       const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>>+					 ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_signal_type_get - get source pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @type: on success - source pin signal type
>>+ *
>>+ * Dpll subsystem callback. Get source pin signal type value.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_signal_type_get(const struct dpll_device
>>*dpll,
>>+					   const struct dpll_pin *pin,
>>+					   enum dpll_pin_signal_type *sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
>>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_signal_type_set - set dpll output pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type to be set
>>+ *
>>+ * dpll subsystem callback. Set source pin signal type value.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_source_signal_type_set(const struct dpll_device *dpll,
>>+				const struct dpll_pin *pin,
>>+				const enum dpll_pin_signal_type sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
>>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_prio_get - get dpll's source prio
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @prio: on success - returns source priority on dpll
>>+ *
>>+ * Dpll subsystem callback. Handler for getting priority of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_prio_get(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin, u32 *prio)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll *d = NULL;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (*prio > ICE_DPLL_PRIO_MAX)
>>+		return ret;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (!p)
>>+		goto unlock;
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		goto unlock;
>>+	*prio = d->input_prio[p->idx];
>>+	ret = 0;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>>+		__func__, dpll, pin, pf, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_prio_set - set dpll source prio
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @prio: source priority to be set on dpll
>>+ *
>>+ * Dpll subsystem callback. Handler for setting priority of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_prio_set(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin, const u32 prio)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll *d = NULL;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (prio > ICE_DPLL_PRIO_MAX)
>>+		return ret;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (!p)
>>+		goto unlock;
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		goto unlock;
>>+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>>+		__func__, dpll, pin, pf, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_mode_active -  check if given pin's mode is active
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ * @pin_type: type of a pin to be checked
>>+ *
>>+ * Handler for checking if given mode is active on a pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_pin_mode_active(const struct dpll_device *dpll,
>>+				     const struct dpll_pin *pin,
>>+				     const enum dpll_pin_mode mode,
>>+				     const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	bool ret = false;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p)
>>+		goto unlock;
>>+	if (test_bit(mode, &p->mode_mask))
>>+		ret = true;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_mode_active - check if rclk source pin's mode is active
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>active
>>+ * on a recovered clock pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_rclk_mode_active(const struct dpll_device *dpll,
>>+				      const struct dpll_pin *pin,
>>+				      const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_mode_active - check if output pin's mode is active
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>active
>>+ * on an output pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_output_mode_active(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>>+					ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_mode_active - check if source pin's mode is active
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>active
>>+ * on a source pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_source_mode_active(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>>+					ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_mode_supported - check if pin's mode is supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ * @pin_type: type of a pin being checked
>>+ *
>>+ * Handler for checking if given mode is supported on a pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_pin_mode_supported(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					const enum dpll_pin_mode mode,
>>+					const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	bool ret = false;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+
>>+	if (!p)
>>+		goto unlock;
>>+	if (test_bit(mode, &p->mode_supported_mask))
>>+		ret = true;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_mode_supported - check if rclk pin's mode is supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>+ * supported on a clock recovery pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_rclk_mode_supported(const struct dpll_device *dpll,
>>+					 const struct dpll_pin *pin,
>>+					 const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>>+					    ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_mode_supported - check if output pin's mode is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>+ * supported on an output pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_output_mode_supported(const struct dpll_device
>>*dpll,
>>+					   const struct dpll_pin *pin,
>>+					   const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_mode_supported - check if source pin's mode is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>+ * supported on a source pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_source_mode_supported(const struct dpll_device
>>*dpll,
>>+					   const struct dpll_pin *pin,
>>+					   const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pin_sig_type_get - get signal type of rclk pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: on success - holds a signal type of a pin
>>+ *
>>+ * DPLL subsystem callback, provides signal type of clock recovery pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success, sig_type value is valid
>>+ * * negative - error
>>+ */
>>+static int ice_dpll_rclk_pin_sig_type_get(const struct dpll_device *dpll,
>>+					  const struct dpll_pin *pin,
>>+					  enum dpll_pin_signal_type *sig_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	int ret = 0;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (!p) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	*sig_type = p->signal_type;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p sig_type:%d
>>ret:%d\n",
>>+		__func__, dpll, pin, pf, *sig_type, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pin_net_if_index_get - get OS interface index callback
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @net_if_idx: on success - holds OS interface index
>>+ *
>>+ * dpll subsystem callback, obtains OS interface index and pass to the
>>caller.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_rclk_pin_net_if_index_get(const struct dpll_device
>>*dpll,
>>+					      const struct dpll_pin *pin,
>>+					      int *net_if_idx)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+
>>+	if (!pf->vsi[0] || pf->vsi[0]->netdev)
>>+		return -EAGAIN;
>>+	*net_if_idx = pf->vsi[0]->netdev->ifindex;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pin_select - select a recovered clock pin as a valid
>>source
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ *
>>+ * dpll subsystem callback, selects a pin for clock recovery,
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_rclk_pin_select(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	u32 freq;
>>+	int ret;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (!p) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	if (!(p->flags & ICE_DPLL_RCLK_SOURCE_FLAG_EN)) {
>>+		ret = -EPERM;
>>+		goto unlock;
>>+	}
>>+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, p->idx, true, &freq);
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>>+		__func__, dpll, pin, pf, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+static struct dpll_pin_ops ice_dpll_rclk_ops = {
>>+	.mode_enable = ice_dpll_rclk_mode_enable,
>>+	.mode_active = ice_dpll_rclk_mode_active,
>>+	.mode_supported = ice_dpll_rclk_mode_supported,
>>+	.signal_type_get = ice_dpll_rclk_pin_sig_type_get,
>>+	.signal_type_supported = ice_dpll_rclk_signal_type_supported,
>>+	.net_if_idx_get = ice_dpll_rclk_pin_net_if_index_get,
>>+	.select = ice_dpll_rclk_pin_select,
>>+};
>>+
>>+static struct dpll_pin_ops ice_dpll_source_ops = {
>>+	.signal_type_get = ice_dpll_source_signal_type_get,
>>+	.signal_type_set = ice_dpll_source_signal_type_set,
>>+	.signal_type_supported = ice_dpll_source_signal_type_supported,
>>+	.mode_active = ice_dpll_source_mode_active,
>>+	.mode_enable = ice_dpll_source_mode_enable,
>>+	.mode_supported = ice_dpll_source_mode_supported,
>>+	.prio_get = ice_dpll_source_prio_get,
>>+	.prio_set = ice_dpll_source_prio_set,
>>+};
>>+
>>+static struct dpll_pin_ops ice_dpll_output_ops = {
>>+	.signal_type_get = ice_dpll_output_signal_type_get,
>>+	.signal_type_set = ice_dpll_output_signal_type_set,
>>+	.signal_type_supported = ice_dpll_output_signal_type_supported,
>>+	.mode_active = ice_dpll_output_mode_active,
>>+	.mode_enable = ice_dpll_output_mode_enable,
>>+	.mode_supported = ice_dpll_output_mode_supported,
>>+};
>>+
>>+static struct dpll_device_ops ice_dpll_ops = {
>>+	.lock_status_get = ice_dpll_lock_status_get,
>>+	.source_pin_idx_get = ice_dpll_source_idx_get,
>>+	.mode_get = ice_dpll_mode_get,
>>+	.mode_supported = ice_dpll_mode_supported,
>>+};
>>+
>>+/**
>>+ * ice_dpll_release_info - release memory allocated for pins
>>+ * @pf: board private structure
>>+ *
>>+ * Release memory allocated for pins by ice_dpll_init_info function.
>>+ */
>>+static void ice_dpll_release_info(struct ice_pf *pf)
>>+{
>>+	kfree(pf->dplls.inputs);
>>+	pf->dplls.inputs = NULL;
>>+	kfree(pf->dplls.outputs);
>>+	pf->dplls.outputs = NULL;
>>+	kfree(pf->dplls.eec.input_prio);
>>+	pf->dplls.eec.input_prio = NULL;
>>+	kfree(pf->dplls.pps.input_prio);
>>+	pf->dplls.pps.input_prio = NULL;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins - initializes source or output pins information
>>+ * @pf: Board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Init information about input or output pins, cache them in pins
>>struct.
>>+ */
>>+static int ice_dpll_init_pins(struct ice_pf *pf,
>>+			      const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>>+	int ret = -EINVAL, num_pins, i;
>>+	struct ice_hw *hw = &pf->hw;
>>+	struct ice_dpll_pin *pins;
>>+	bool input;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		input = true;
>>+		pins = pf->dplls.inputs;
>>+		num_pins = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		input = false;
>>+		pins = pf->dplls.outputs;
>>+		num_pins = pf->dplls.num_outputs;
>>+	} else {
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < num_pins; i++) {
>>+		pins[i].idx = i;
>>+		pins[i].name = ice_cgu_get_pin_name(hw, i, input);
>>+		pins[i].type = ice_cgu_get_pin_type(hw, i, input);
>>+		set_bit(DPLL_PIN_MODE_CONNECTED,
>>+			&pins[i].mode_supported_mask);
>>+		set_bit(DPLL_PIN_MODE_DISCONNECTED,
>>+			&pins[i].mode_supported_mask);
>>+		if (input) {
>>+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>>+						      &de->input_prio[i]);
>>+			if (ret)
>>+				return ret;
>>+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>>+						      &dp->input_prio[i]);
>>+			if (ret)
>>+				return ret;
>>+			set_bit(DPLL_PIN_MODE_SOURCE,
>>+				&pins[i].mode_supported_mask);
>>+		} else {
>>+			set_bit(DPLL_PIN_MODE_OUTPUT,
>>+				&pins[i].mode_supported_mask);
>>+		}
>>+		pins[i].signal_type_mask =
>>+				ice_cgu_get_pin_sig_type_mask(hw, i, input);
>>+		ret = ice_dpll_pin_update(hw, &pins[i], pin_type);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_release_pins - release pin's from dplls registered in
>>subsystem
>>+ * @dpll_eec: dpll_eec dpll pointer
>>+ * @dpll_pps: dpll_pps dpll pointer
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ *
>>+ * Deregister and free pins of a given array of pins from dpll devices
>>registered
>>+ * in dpll subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * positive - number of errors encounterd on pin's deregistration.
>>+ */
>>+static int
>>+ice_dpll_release_pins(struct dpll_device *dpll_eec,
>>+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
>>+		      int count)
>>+{
>>+	int i, ret, err;
>>+
>>+	for (i = 0; i < count; i++) {
>>+		struct ice_dpll_pin *p = &pins[i];
>>+
>>+		if (p && p->pin) {
>>+			if (dpll_eec) {
>>+				ret = dpll_pin_deregister(dpll_eec, p->pin);
>>+				if (ret)
>>+					err++;
>>+			}
>>+			if (dpll_pps) {
>>+				ret = dpll_pin_deregister(dpll_pps, p->pin);
>>+				if (ret)
>>+					err++;
>>+			}
>>+			dpll_pin_free(p->pin);
>>+			p->pin = NULL;
>>+		}
>>+	}
>>+
>>+	return err;
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_pins - register pins with a dpll
>>+ * @pf: board private structure
>>+ * @dpll: registered dpll pointer
>>+ * @pin_type: type of pins being registered
>>+ *
>>+ * Register source or output pins within given DPLL in a Linux dpll
>>subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_register_pins(struct ice_pf *pf, struct dpll_device *dpll,
>>+		       const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	struct dpll_pin_ops *ops;
>>+	int ret, i, count;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		ops = &ice_dpll_source_ops;
>>+		pins = pf->dplls.inputs;
>>+		count = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		ops = &ice_dpll_output_ops;
>>+		pins = pf->dplls.outputs;
>>+		count = pf->dplls.num_outputs;
>>+	} else {
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < count; i++) {
>>+		pins[i].pin = dpll_pin_alloc(pins[i].name, pins[i].type);
>>+		if (IS_ERR_OR_NULL(pins[i].pin))
>>+			return -ENOMEM;
>>+
>>+		ret = dpll_pin_register(dpll, pins[i].pin, ops, pf);
>>+		if (ret)
>>+			return -ENOSPC;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_shared_pins - register shared pins in DPLL subsystem
>>+ * @pf: board private structure
>>+ * @dpll_o: registered dpll pointer (owner)
>>+ * @dpll: registered dpll pointer
>>+ * @type: type of pins being registered
>>+ *
>>+ * Register pins from given owner dpll within given dpll in Linux dpll
>>subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_register_shared_pins(struct ice_pf *pf, struct dpll_device
>>*dpll_o,
>>+			      struct dpll_device *dpll,
>>+			      const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	struct dpll_pin_ops *ops;
>>+	int ret, i, count;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		ops = &ice_dpll_source_ops;
>>+		pins = pf->dplls.inputs;
>>+		count = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		ops = &ice_dpll_output_ops;
>>+		pins = pf->dplls.outputs;
>>+		count = pf->dplls.num_outputs;
>>+	} else {
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < count; i++) {
>>+		ret = dpll_shared_pin_register(dpll_o, dpll, pins[i].name,
>>+					       ops, pf);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_info - prepare pf's dpll information structure
>>+ * @pf: board private structure
>>+ *
>>+ * Acquire (from HW) and set basic dpll information (on pf->dplls
>>struct).
>>+ *
>>+ * Return:
>>+ *  0 - success
>>+ *  negative - error
>>+ */
>>+static int ice_dpll_init_info(struct ice_pf *pf)
>>+{
>>+	struct ice_aqc_get_cgu_abilities abilities;
>>+	struct ice_dpll *de = &pf->dplls.eec;
>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_hw *hw = &pf->hw;
>>+	int ret, alloc_size;
>>+
>>+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>>+	if (ret) {
>>+		dev_err(ice_pf_to_dev(pf),
>>+			"err:%d %s failed to read cgu abilities\n",
>>+			ret, ice_aq_str(hw->adminq.sq_last_status));
>>+		return ret;
>>+	}
>>+
>>+	de->dpll_idx = abilities.eec_dpll_idx;
>>+	dp->dpll_idx = abilities.pps_dpll_idx;
>>+	d->num_inputs = abilities.num_inputs;
>>+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>>+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!d->inputs)
>>+		return -ENOMEM;
>>+
>>+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>>+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!de->input_prio)
>>+		return -ENOMEM;
>>+
>>+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!dp->input_prio)
>>+		return -ENOMEM;
>>+
>>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (ret)
>>+		goto release_info;
>>+
>>+	d->num_outputs = abilities.num_outputs;
>>+	alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>+	d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!d->outputs)
>>+		goto release_info;
>>+
>>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+	if (ret)
>>+		goto release_info;
>>+
>>+	dev_dbg(ice_pf_to_dev(pf), "%s - success, inputs:%u, outputs:%u\n",
>>__func__,
>>+		abilities.num_inputs, abilities.num_outputs);
>>+
>>+	return 0;
>>+
>>+release_info:
>>+	dev_err(ice_pf_to_dev(pf),
>>+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p,
>>d->outputs:%p\n",
>>+		__func__, d->inputs, de->input_prio,
>>+		dp->input_prio, d->outputs);
>>+	ice_dpll_release_info(pf);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_generate_clock_id - generates unique clock_id for registering
>>dpll.
>>+ * @pf: board private structure
>>+ * @clock_id: holds generated clock_id
>>+ *
>>+ * Generates unique (per board) clock_id for allocation and search of
>>dpll
>>+ * devices in Linux dpll subsystem.
>>+ */
>>+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
>>+{
>>+	*clock_id = pci_get_dsn(pf->pdev);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_dpll
>>+ * @pf: board private structure
>>+ *
>>+ * Allocate and register dpll in dpll subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - allocation fails
>>+ */
>>+static int ice_dpll_init_dpll(struct ice_pf *pf)
>>+{
>>+	struct device *dev = ice_pf_to_dev(pf);
>>+	struct ice_dpll *de = &pf->dplls.eec;
>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>+	u64 clock_id = 0;
>>+	int ret = 0;
>>+
>>+	ice_generate_clock_id(pf, &clock_id);
>>+
>>+	de->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_EEC,
>>+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
>>+	if (!de->dpll) {
>>+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (eec)\n");
>>+		return -ENOMEM;
>>+	}
>>+
>>+	dp->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_PPS,
>>+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
>>+	if (!dp->dpll) {
>>+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (pps)\n");
>>+		return -ENOMEM;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_update_state
>>+ * @hw: board private structure
>>+ * @d: pointer to queried dpll device
>>+ *
>>+ * Poll current state of dpll from hw and update ice_dpll struct.
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - AQ failure
>>+ */
>>+static int ice_dpll_update_state(struct ice_hw *hw, struct ice_dpll *d)
>>+{
>>+	int ret;
>>+
>>+	ret = ice_get_cgu_state(hw, d->dpll_idx, d->prev_dpll_state,
>>+				&d->source_idx, &d->ref_state, &d->eec_mode,
>>+				&d->phase_offset, &d->dpll_state);
>>+
>>+	dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+		"update dpll=%d, src_idx:%u, state:%d, prev:%d\n",
>>+		d->dpll_idx, d->source_idx,
>>+		d->dpll_state, d->prev_dpll_state);
>>+
>>+	if (ret)
>>+		dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+			"update dpll=%d state failed, ret=%d %s\n",
>>+			d->dpll_idx, ret,
>>+			ice_aq_str(hw->adminq.sq_last_status));
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_notify_changes - notify dpll subsystem about changes
>>+ * @d: pointer do dpll
>>+ *
>>+ * Once change detected appropriate event is submitted to the dpll
>>subsystem.
>>+ */
>>+static void ice_dpll_notify_changes(struct ice_dpll *d)
>>+{
>>+	if (d->prev_dpll_state != d->dpll_state) {
>>+		d->prev_dpll_state = d->dpll_state;
>>+		dpll_device_notify(d->dpll, DPLL_CHANGE_LOCK_STATUS);
>>+	}
>>+	if (d->prev_source_idx != d->source_idx) {
>>+		d->prev_source_idx = d->source_idx;
>>+		dpll_device_notify(d->dpll, DPLL_CHANGE_SOURCE_PIN);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>+ * @work: pointer to kthread_work structure
>>+ *
>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>+ */
>>+static void ice_dpll_periodic_work(struct kthread_work *work)
>>+{
>>+	struct ice_dplls *d = container_of(work, struct ice_dplls,
>>work.work);
>>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>>+	struct ice_dpll *de = &pf->dplls.eec;
>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>+	int ret = 0;
>>+
>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>
>Why do you need to check the flag there, this would should not be
>ever scheduled in case the flag was not set.
>

It's here rather for stopping the worker, It shall no longer reschedule and
bail out.

>
>>+		return;
>>+	mutex_lock(&d->lock);
>>+	ret = ice_dpll_update_state(&pf->hw, de);
>>+	if (!ret)
>>+		ret = ice_dpll_update_state(&pf->hw, dp);
>>+	if (ret) {
>>+		d->cgu_state_acq_err_num++;
>>+		/* stop rescheduling this worker */
>>+		if (d->cgu_state_acq_err_num >
>>+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
>>+			dev_err(ice_pf_to_dev(pf),
>>+				"EEC/PPS DPLLs periodic work disabled\n");
>>+			return;
>>+		}
>>+	}
>>+	mutex_unlock(&d->lock);
>>+	ice_dpll_notify_changes(de);
>>+	ice_dpll_notify_changes(dp);
>>+
>>+	/* Run twice a second or reschedule if update failed */
>>+	kthread_queue_delayed_work(d->kworker, &d->work,
>>+				   ret ? msecs_to_jiffies(10) :
>>+				   msecs_to_jiffies(500));
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>>+ * @pf: board private structure
>>+ *
>>+ * Create and start DPLLs periodic worker.
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - create worker failure
>>+ */
>>+static int ice_dpll_init_worker(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct kthread_worker *kworker;
>>+
>>+	ice_dpll_update_state(&pf->hw, &d->eec);
>>+	ice_dpll_update_state(&pf->hw, &d->pps);
>>+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>>+	kworker = kthread_create_worker(0, "ice-dplls-%s",
>>+					dev_name(ice_pf_to_dev(pf)));
>>+	if (IS_ERR(kworker))
>>+		return PTR_ERR(kworker);
>>+	d->kworker = kworker;
>>+	d->cgu_state_acq_err_num = 0;
>>+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * __ice_dpll_release - Disable the driver/HW support for DPLL and
>>unregister
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * This function handles the cleanup work required from the
>>initialization by
>>+ * freeing resources and unregistering the dpll.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void __ice_dpll_release(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_dpll *de = &d->eec;
>>+	struct ice_dpll *dp = &d->pps;
>>+	int ret;
>>+
>>+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
>>+				    d->num_inputs);
>>+	if (ret)
>>+		dev_warn(ice_pf_to_dev(pf),
>>+			 "pin deregister on PPS dpll err=%d\n", ret);
>>+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
>>+				    d->num_outputs);
>>+	if (ret)
>>+		dev_warn(ice_pf_to_dev(pf),
>>+			 "pin deregister on PPS dpll err=%d\n", ret);
>>+	ice_dpll_release_info(pf);
>>+	if (dp->dpll) {
>>+		dpll_device_unregister(dp->dpll);
>>+		dpll_device_free(dp->dpll);
>>+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
>>+	}
>>+
>>+	if (de->dpll) {
>>+		dpll_device_unregister(de->dpll);
>>+		dpll_device_free(de->dpll);
>>+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
>>+	}
>>+
>>+	kthread_cancel_delayed_work_sync(&d->work);
>>+	if (d->kworker) {
>>+		kthread_destroy_worker(d->kworker);
>>+		d->kworker = NULL;
>>+		dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_init - Initialize DPLLs support
>>+ * @pf: board private structure
>>+ *
>>+ * Set up the device as owner of DPLLs registering them and pins
>>connected
>>+ * within Linux dpll subsystem. Allow userpsace to obtain state of DPLL
>>+ * and handling of DPLL configuration requests.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+int ice_dpll_init(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+	int err;
>>+
>>+	mutex_init(&d->lock);
>>+	mutex_lock(&d->lock);
>
>It is always odd to see the lock being created and locked right away.
>Why do you need to lock it here?
>

Once dpll is registered it can start sending requests, we want to have fully
initialized dpll/pins before that. Lock could be done after ice_dpll_init_info.

>
>>+	err = ice_dpll_init_info(pf);
>>+	if (err)
>>+		goto unlock;
>>+	err = ice_dpll_init_dpll(pf);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_pins(pf, d->eec.dpll,
>>ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_pins(pf, d->eec.dpll,
>>ICE_DPLL_PIN_TYPE_OUTPUT);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
>>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
>>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>>+	if (err)
>>+		goto release;
>>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>>+	err = ice_dpll_init_worker(pf);
>>+	if (err)
>>+		goto release;
>>+	mutex_unlock(&d->lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "DPLLs init successful\n");
>>+
>>+	return err;
>>+release:
>>+	__ice_dpll_release(pf);
>>+unlock:
>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+	mutex_unlock(&d->lock);
>>+	mutex_destroy(&d->lock);
>>+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure\n");
>>+
>>+	return err;
>>+}
>>+
>>+/**
>>+ * ice_dpll_release - Disable the driver/HW support for DPLLs and
>>unregister
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * This function handles the cleanup work required from the
>>initialization by
>>+ * freeing resources and unregistering the dpll.
>>+ */
>>+void ice_dpll_release(struct ice_pf *pf)
>>+{
>>+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>+		mutex_lock(&pf->dplls.lock);
>>+		clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+		__ice_dpll_release(pf);
>>+		mutex_unlock(&pf->dplls.lock);
>>+		mutex_destroy(&pf->dplls.lock);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pin_init - init the pin info for recovered clock
>>+ * @attr: structure with pin attributes
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+void ice_dpll_rclk_pin_init(struct ice_dpll_pin *p)
>>+{
>>+	p->flags = ICE_DPLL_RCLK_SOURCE_FLAG_EN;
>>+	p->type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>>+	set_bit(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ, &p->signal_type_mask);
>>+	set_bit(DPLL_PIN_MODE_CONNECTED,	  &p->mode_supported_mask);
>>+	set_bit(DPLL_PIN_MODE_DISCONNECTED,	  &p->mode_supported_mask);
>>+	set_bit(DPLL_PIN_MODE_SOURCE,		  &p->mode_supported_mask);
>>+	ice_dpll_pin_update(0, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * __ice_dpll_rclk_release - unregister the recovered pin for dpll device
>>+ * @pf: board private structure
>>+ *
>>+ * This function handles the cleanup work required from the
>>initialization by
>>+ * freeing resources and unregistering the recovered pin.
>>+ */
>>+void __ice_dpll_rclk_release(struct ice_pf *pf)
>>+{
>>+	int ret = 0;
>>+
>>+	if (pf->dplls.eec.dpll) {
>>+		if (pf->dplls.rclk[0].pin)
>>+			ret = dpll_pin_deregister(pf->dplls.eec.dpll,
>>+						  pf->dplls.rclk[0].pin);
>>+		dpll_pin_free(pf->dplls.rclk->pin);
>>+		kfree(pf->dplls.rclk);
>>+	}
>>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pins_init - init the pin for recovered clock
>>+ * @pf: board private structure
>>+ * @first_parent: pointer to a first parent pin
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+int ice_dpll_rclk_pins_init(struct ice_pf *pf, struct ice_dpll_pin
>>*first_parent)
>>+{
>>+	struct ice_dpll_pin *parent, *p;
>>+	char *name;
>>+	int i, ret;
>>+
>>+	if (pf->dplls.rclk)
>>+		return -EEXIST;
>>+	pf->dplls.rclk = kcalloc(pf->dplls.num_rclk, sizeof(*pf->dplls.rclk),
>>+				 GFP_KERNEL);
>>+	if (!pf->dplls.rclk)
>>+		goto release;
>>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>>+		p = &pf->dplls.rclk[i];
>>+		if (!p)
>>+			goto release;
>>+		ice_dpll_rclk_pin_init(p);
>>+		parent = first_parent + i;
>>+		if (!parent)
>>+			goto release;
>>+		p->idx = i;
>>+		name = kcalloc(DPLL_PIN_DESC_LEN, sizeof(*p->name),
>>GFP_KERNEL);
>>+		if (!name)
>>+			goto release;
>>+		snprintf(name, DPLL_PIN_DESC_LEN - 1, "%s-%u",
>>+			 parent->name, pf->hw.pf_id);
>>+		p->name = name;
>>+		p->pin = dpll_pin_alloc(p->name, p->type);
>>+		if (IS_ERR_OR_NULL(p->pin))
>>+			goto release;
>>+		ret = dpll_muxed_pin_register(pf->dplls.eec.dpll, parent->name,
>>+					      p->pin, &ice_dpll_rclk_ops, pf);
>>+		if (ret)
>>+			goto release;
>>+		ret = dpll_shared_pin_register(pf->dplls.eec.dpll,
>>+					       pf->dplls.pps.dpll,
>>+					       p->name,
>>+					       &ice_dpll_rclk_ops, pf);
>>+		if (ret)
>>+			goto release;
>>+	}
>>+
>>+	return ret;
>>+release:
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s releasing - p: %p, parent:%p, p->pin:%p name:%s, ret:%d\n",
>>+		__func__, p, parent, p->pin, name, ret);
>>+	__ice_dpll_rclk_release(pf);
>>+	return -ENOMEM;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
>>+ * @pf: board private structure
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf)
>>+{
>>+	u64 clock_id = 0;
>>+
>>+	ice_generate_clock_id(pf, &clock_id);
>>+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,
>
>I have to say I'm a bit lost in this code. Why exactly do you need this
>here? Looks like the pointer was set in ice_dpll_init_dpll().
>
>Or, is that in case of a different PF instantiating the DPLL instances?

Yes it is, different PF is attaching recovered clock pins with this.

>If yes, I'm pretty sure what it is wrong. What is the PF which did
>instanticate those unbinds? You have to share the dpll instance,
>refcount it.
>

It will break, as in our case only one designated PF controls the dpll.

>Btw, you have a problem during init as well, as the order matters. What
>if the other function probes only after executing this? You got -EFAULT
>here and bail out.
>

We don't expect such use case, altough I see your point, will try to fix it.

>In mlx5, I also share one dpll instance between 2 PFs. What I do is I
>create mlx5-dpll instance which is refcounted, created by first probed
>PF and removed by the last one. In mlx5 case, the PFs are equal, nobody
>is an owner of the dpll. In your case, I think it is different. So
>probably better to implement the logic in driver then in the dpll core.
>

For this NIC only one PF will control the dpll, so there is a designated owner.
Here owner must not only initialize a dpll but also register its pin,
so the other PFs could register additional pins. Basically it means
for ice that we can only rely on some postponed rclk initialization for
a case of unordered PF initialization. Seems doable.

>Then you don't need dpll_device_get_by_clock_id at all. If you decide to
>implement that in dpll core, I believe that there should be some
>functions like:
>dpll = dpll_device_get(ops, clock_id, ...)  - to create/get reference
>dpll_device_put(dpll)                       - to put reference/destroy

Sure, we can rename "dpll_device_get_by_clock_id" to "dpll_device_get" (as
it is only function currently exported for such behavior), and add
"dpll_device_put", with ref counts as suggested.

>
>First caller of dpll_device_get() actually makes dpll to instantiate the
>device.
>

Maybe I am missing something.. do you suggest that "dpll_device_get" would
allocate dpll device and do ref count, and then dpll_device_register(..) call
would assign all the arguments that are available only in the owner instance?
Or i got it wrong?

>
>
>>+							 DPLL_TYPE_EEC, 0);
>>+	if (!pf->dplls.eec.dpll)
>>+		return -EFAULT;
>>+	pf->dplls.pps.dpll = dpll_device_get_by_clock_id(clock_id,
>>+							 DPLL_TYPE_PPS, 0);
>>+	if (!pf->dplls.pps.dpll)
>>+		return -EFAULT;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_parent_pins_init - initialize the recovered clock parent
>>pins
>>+ * @pf: board private structure
>>+ * @base_rclk_idx: number of first recovered clock pin in DPLL
>>+ *
>>+ * This function shall be executed only if ICE_FLAG_DPLL feature is not
>
>Feature? It's a flag.
>

Sure, will fix.

Thanks,
Arkadiusz

>
>>+ * supported.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+static int ice_dpll_rclk_parent_pins_init(struct ice_pf *pf, u8
>>base_rclk_idx)
>>+{
>>+	int i;
>>+
>>+	if (pf->dplls.inputs)
>>+		return -EINVAL;
>>+	pf->dplls.inputs = kcalloc(pf->dplls.num_rclk,
>>+				   sizeof(*pf->dplls.inputs), GFP_KERNEL);
>>+
>>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>>+		const char *desc;
>>+
>>+		desc = ice_cgu_get_pin_name(&pf->hw, base_rclk_idx + i, true);
>>+		if (!desc)
>>+			return -EINVAL;
>>+		pf->dplls.inputs[i].name = desc;
>>+	}
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_init - Enable support for DPLL's PHY clock recovery
>>+ * @pf: board private structure
>>+ *
>>+ * Context:
>>+ * Acquires a pf->dplls.lock. If PF is not an owner of DPLL it shall find
>>and
>>+ * connect its pins with the device dpll.
>>+ *
>>+ * This function handles enablement of PHY clock recovery part for
>>timesync
>>+ * capabilities.
>>+ * Prepares and initalizes resources required to register its PHY clock
>sources
>>+ * within DPLL subsystem.
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+int ice_dpll_rclk_init(struct ice_pf *pf)
>>+{
>>+	struct ice_dpll_pin *first_parent = NULL;
>>+	u8 base_rclk_idx;
>>+	int ret;
>>+
>>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &base_rclk_idx,
>>+					&pf->dplls.num_rclk);
>>+	if (ret)
>>+		return ret;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>+		ret = ice_dpll_rclk_find_dplls(pf);
>>+		dev_dbg(ice_pf_to_dev(pf), "ecc:%p, pps:%p\n",
>>+			pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>>+		if (ret)
>>+			goto unlock;
>>+		ret = ice_dpll_rclk_parent_pins_init(pf, base_rclk_idx);
>>+		if (ret)
>>+			goto unlock;
>>+		first_parent = &pf->dplls.inputs[0];
>>+	} else {
>>+		first_parent = &pf->dplls.inputs[base_rclk_idx];
>>+	}
>>+	if (!first_parent) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	ret = ice_dpll_rclk_pins_init(pf, first_parent);
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK init ret=%d\n", ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_release - Disable the support for DPLL's PHY clock
>>recovery
>>+ * @pf: board private structure
>>+ *
>>+ * Context:
>>+ * Acquires a pf->dplls.lock. Requires dplls to be present, must be
>called
>>+ * before dplls are realesed.
>>+ *
>>+ * This function handles the cleanup work of resources allocated for
>>enablement
>>+ * of PHY recovery clock mechanics.
>>+ * Unregisters RCLK pins and frees pin's memory allocated by
>>ice_dpll_rclk_init.
>>+ */
>>+void ice_dpll_rclk_release(struct ice_pf *pf)
>>+{
>>+	int i, ret = 0;
>>+
>>+	if (!pf->dplls.rclk)
>>+		return;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>>+		if (pf->dplls.rclk[i].pin) {
>>+			dpll_pin_deregister(pf->dplls.eec.dpll,
>>+					    pf->dplls.rclk[i].pin);
>>+			dpll_pin_deregister(pf->dplls.pps.dpll,
>>+					    pf->dplls.rclk[i].pin);
>>+			dpll_pin_free(pf->dplls.rclk[i].pin);
>>+			pf->dplls.rclk[i].pin = NULL;
>>+		}
>>+		kfree(pf->dplls.rclk[i].name);
>>+		pf->dplls.rclk[i].name = NULL;
>>+	}
>>+	/* inputs were prepared only for RCLK, release them here */
>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>+		kfree(pf->dplls.inputs);
>>+		pf->dplls.inputs = NULL;
>>+	}
>>+	kfree(pf->dplls.rclk);
>>+	pf->dplls.rclk = NULL;
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
>>+}
>>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h
>>b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>new file mode 100644
>>index 000000000000..3390d60f2fab
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>@@ -0,0 +1,99 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/* Copyright (C) 2022, Intel Corporation. */
>>+
>>+#ifndef _ICE_DPLL_H_
>>+#define _ICE_DPLL_H_
>>+
>>+#include "ice.h"
>>+
>>+#define ICE_DPLL_PRIO_MAX	0xF
>>+
>>+/** ice_dpll_pin - store info about pins
>>+ * @pin: dpll pin structure
>>+ * @flags: pin flags returned from HW
>>+ * @idx: ice pin private idx
>>+ * @type: type of a pin
>>+ * @signal_type: current signal type
>>+ * @signal_type_mask: signal types supported
>>+ * @freq: current frequency of a pin
>>+ * @mode_mask: current pin modes as bitmask
>>+ * @mode_supported_mask: supported pin modes
>>+ * @name: pin name
>>+ */
>>+struct ice_dpll_pin {
>>+	struct dpll_pin *pin;
>>+#define ICE_DPLL_RCLK_SOURCE_FLAG_EN	BIT(0)
>>+	u8 flags;
>>+	u8 idx;
>>+	enum dpll_pin_type type;
>>+	enum dpll_pin_signal_type signal_type;
>>+	unsigned long signal_type_mask;
>>+	u32 freq;
>>+	unsigned long mode_mask;
>>+	unsigned long mode_supported_mask;
>>+	const char *name;
>>+};
>>+
>>+/** ice_dpll - store info required for DPLL control
>>+ * @dpll: pointer to dpll dev
>>+ * @dpll_idx: index of dpll on the NIC
>>+ * @source_idx: source currently selected
>>+ * @prev_source_idx: source previously selected
>>+ * @ref_state: state of dpll reference signals
>>+ * @eec_mode: eec_mode dpll is configured for
>>+ * @phase_offset: phase delay of a dpll
>>+ * @input_prio: priorities of each input
>>+ * @dpll_state: current dpll sync state
>>+ * @prev_dpll_state: last dpll sync state
>>+ */
>>+struct ice_dpll {
>>+	struct dpll_device *dpll;
>>+	int dpll_idx;
>>+	u8 source_idx;
>>+	u8 prev_source_idx;
>>+	u8 ref_state;
>>+	u8 eec_mode;
>>+	s64 phase_offset;
>>+	u8 *input_prio;
>>+	enum ice_cgu_state dpll_state;
>>+	enum ice_cgu_state prev_dpll_state;
>>+};
>>+
>>+/** ice_dplls - store info required for CCU (clock controlling unit)
>>+ * @kworker: periodic worker
>>+ * @work: periodic work
>>+ * @lock: locks access to configuration of a dpll
>>+ * @eec: pointer to EEC dpll dev
>>+ * @pps: pointer to PPS dpll dev
>>+ * @inputs: input pins pointer
>>+ * @outputs: output pins pointer
>>+ * @rclk: recovered pins pointer
>>+ * @num_inputs: number of input pins available on dpll
>>+ * @num_outputs: number of output pins available on dpll
>>+ * @num_rclk: number of recovered clock pins available on dpll
>>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>>+ */
>>+struct ice_dplls {
>>+	struct kthread_worker *kworker;
>>+	struct kthread_delayed_work work;
>>+	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 *rclk;
>>+	u32 num_inputs;
>>+	u32 num_outputs;
>>+	u8 num_rclk;
>>+	int cgu_state_acq_err_num;
>>+};
>>+
>>+int ice_dpll_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_release(struct ice_pf *pf);
>>+
>>+int ice_dpll_rclk_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_rclk_release(struct ice_pf *pf);
>>+
>>+#endif
>>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c
>>b/drivers/net/ethernet/intel/ice/ice_main.c
>>index a9a7f8b52140..8b65f4ad245e 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>>@@ -4896,6 +4896,12 @@ ice_probe(struct pci_dev *pdev, const struct
>>pci_device_id __always_unused *ent)
>> 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
>> 		ice_ptp_init(pf);
>>
>>+	if (ice_is_feature_supported(pf, ICE_F_CGU))
>>+		ice_dpll_init(pf);
>>+
>>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>>+		ice_dpll_rclk_init(pf);
>>+
>> 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
>> 		ice_gnss_init(pf);
>>
>>@@ -5078,6 +5084,10 @@ static void ice_remove(struct pci_dev *pdev)
>> 		ice_ptp_release(pf);
>> 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
>> 		ice_gnss_exit(pf);
>>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>>+		ice_dpll_rclk_release(pf);
>>+	if (ice_is_feature_supported(pf, ICE_F_CGU))
>>+		ice_dpll_release(pf);
>> 	if (!ice_is_safe_mode(pf))
>> 		ice_remove_arfs(pf);
>> 	ice_setup_mc_magic_wake(pf);
>>--
>>2.30.2
>>

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

* RE: [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
@ 2023-01-27 18:13       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-27 18:13 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk, Olech, Milena, Michalik, Michal


>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, January 19, 2023 3:54 PM
>
>Tue, Jan 17, 2023 at 07:00:51PM CET, vadfed@meta.com wrote:
>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>
>>Control over clock generation unit is required for further development
>>of Synchronous Ethernet feature. Interface provides ability to obtain
>>current state of a dpll, its sources and outputs which are pins, and
>>allows their configuration.
>>
>>Co-developed-by: Milena Olech <milena.olech@intel.com>
>>Signed-off-by: Milena Olech <milena.olech@intel.com>
>>Co-developed-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Michal Michalik <michal.michalik@intel.com>
>>Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>>Signed-off-by: Vadim Fedorenko <vadfed@meta.com>
>>---
>> drivers/net/ethernet/intel/Kconfig        |    1 +
>> drivers/net/ethernet/intel/ice/Makefile   |    3 +-
>> drivers/net/ethernet/intel/ice/ice.h      |    4 +
>> drivers/net/ethernet/intel/ice/ice_dpll.c | 2115 +++++++++++++++++++++
>> drivers/net/ethernet/intel/ice/ice_dpll.h |   99 +
>> drivers/net/ethernet/intel/ice/ice_main.c |   10 +
>> 6 files changed, 2231 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
>> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
>>
>>diff --git a/drivers/net/ethernet/intel/Kconfig
>>b/drivers/net/ethernet/intel/Kconfig
>>index 3facb55b7161..dcf0e12991bf 100644
>>--- a/drivers/net/ethernet/intel/Kconfig
>>+++ b/drivers/net/ethernet/intel/Kconfig
>>@@ -300,6 +300,7 @@ config ICE
>> 	select DIMLIB
>> 	select NET_DEVLINK
>> 	select PLDMFW
>>+	select DPLL
>> 	help
>> 	  This driver supports Intel(R) Ethernet Connection E800 Series of
>> 	  devices.  For more information on how to identify your adapter, go
>>diff --git a/drivers/net/ethernet/intel/ice/Makefile
>>b/drivers/net/ethernet/intel/ice/Makefile
>>index 9183d480b70b..ebfd456f76cd 100644
>>--- a/drivers/net/ethernet/intel/ice/Makefile
>>+++ b/drivers/net/ethernet/intel/ice/Makefile
>>@@ -32,7 +32,8 @@ ice-y := ice_main.o	\
>> 	 ice_lag.o	\
>> 	 ice_ethtool.o  \
>> 	 ice_repr.o	\
>>-	 ice_tc_lib.o
>>+	 ice_tc_lib.o	\
>>+	 ice_dpll.o
>> ice-$(CONFIG_PCI_IOV) +=	\
>> 	ice_sriov.o		\
>> 	ice_virtchnl.o		\
>>diff --git a/drivers/net/ethernet/intel/ice/ice.h
>>b/drivers/net/ethernet/intel/ice/ice.h
>>index 3368dba42789..efc1844ef77c 100644
>>--- a/drivers/net/ethernet/intel/ice/ice.h
>>+++ b/drivers/net/ethernet/intel/ice/ice.h
>>@@ -73,6 +73,7 @@
>> #include "ice_lag.h"
>> #include "ice_vsi_vlan_ops.h"
>> #include "ice_gnss.h"
>>+#include "ice_dpll.h"
>>
>> #define ICE_BAR0		0
>> #define ICE_REQ_DESC_MULTIPLE	32
>>@@ -198,6 +199,7 @@
>> enum ice_feature {
>> 	ICE_F_DSCP,
>> 	ICE_F_PTP_EXTTS,
>>+	ICE_F_PHY_RCLK,
>> 	ICE_F_SMA_CTRL,
>> 	ICE_F_CGU,
>> 	ICE_F_GNSS,
>>@@ -509,6 +511,7 @@ enum ice_pf_flags {
>> 	ICE_FLAG_PLUG_AUX_DEV,
>> 	ICE_FLAG_MTU_CHANGED,
>> 	ICE_FLAG_GNSS,			/* GNSS successfully initialized */
>>+	ICE_FLAG_DPLL,			/* SyncE/PTP dplls initialized */
>> 	ICE_PF_FLAGS_NBITS		/* must be last */
>> };
>>
>>@@ -633,6 +636,7 @@ struct ice_pf {
>> #define ICE_VF_AGG_NODE_ID_START	65
>> #define ICE_MAX_VF_AGG_NODES		32
>> 	struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
>>+	struct ice_dplls dplls;
>> };
>>
>> struct ice_netdev_priv {
>>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c
>>b/drivers/net/ethernet/intel/ice/ice_dpll.c
>>new file mode 100644
>>index 000000000000..6ed1fcee4e03
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>>@@ -0,0 +1,2115 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/* Copyright (C) 2022, Intel Corporation. */
>>+
>>+#include "ice.h"
>>+#include "ice_lib.h"
>>+#include "ice_trace.h"
>>+#include <linux/dpll.h>
>>+#include <uapi/linux/dpll.h>
>
>Don't include uapi header directly. Inlude of linux/dpll.h is enough.
>

True, will fix.

>
>>+
>>+#define CGU_STATE_ACQ_ERR_THRESHOLD	50
>>+#define ICE_DPLL_FREQ_1_HZ		1
>>+#define ICE_DPLL_FREQ_10_MHZ		10000000
>>+
>>+/**
>>+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock
>>status
>>+ */
>>+static const enum dpll_lock_status
>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>
>Don't use __ define.
>

Sure, will fix.

>
>>+	[ICE_CGU_STATE_INVALID] = DPLL_LOCK_STATUS_UNSPEC,
>>+	[ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>+	[ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_CALIBRATING,
>>+	[ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED,
>>+	[ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>+};
>>+
>>+/**
>>+ * ice_dpll_pin_type - enumerate ince pin types
>>+ */
>>+enum ice_dpll_pin_type {
>>+	ICE_DPLL_PIN_INVALID = 0,
>>+	ICE_DPLL_PIN_TYPE_SOURCE,
>>+	ICE_DPLL_PIN_TYPE_OUTPUT,
>>+	ICE_DPLL_PIN_TYPE_RCLK_SOURCE,
>>+};
>>+
>>+/**
>>+ * pin_type_name - string names of ice pin types
>>+ */
>>+static const char * const pin_type_name[] = {
>>+	[ICE_DPLL_PIN_TYPE_SOURCE] = "source",
>>+	[ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>>+	[ICE_DPLL_PIN_TYPE_RCLK_SOURCE] = "rclk-source",
>>+};
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_to_freq - translate pin_signal_type to freq
>>value
>>+ * @sig_type: signal type to find frequency
>>+ * @freq: on success - frequency of a signal type
>>+ *
>>+ * Return:
>>+ * * 0 - frequency valid
>>+ * * negative - error
>>+ */
>>+static inline int
>>+ice_dpll_pin_signal_type_to_freq(const enum dpll_pin_signal_type
>>sig_type,
>>+				 u32 *freq)
>>+{
>>+	if (sig_type == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>>+		return -EINVAL;
>>+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_1_PPS)
>>+		*freq = ICE_DPLL_FREQ_1_HZ;
>>+	else if (sig_type == DPLL_PIN_SIGNAL_TYPE_10_MHZ)
>>+		*freq = ICE_DPLL_FREQ_10_MHZ;
>>+	else
>>+		return -EINVAL;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_freq_to_signal_type - translate pin freq to signal type
>>+ * @freq: frequency to translate
>>+ *
>>+ * Return: signal type of a pin based on frequency.
>>+ */
>>+static inline enum dpll_pin_signal_type
>>+ice_dpll_pin_freq_to_signal_type(u32 freq)
>>+{
>>+	if (freq == ICE_DPLL_FREQ_1_HZ)
>>+		return DPLL_PIN_SIGNAL_TYPE_1_PPS;
>>+	else if (freq == ICE_DPLL_FREQ_10_MHZ)
>>+		return DPLL_PIN_SIGNAL_TYPE_10_MHZ;
>>+	else
>>+		return DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_set - set pin's signal type in hardware
>>+ * @pf: Board private structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being configured
>>+ * @sig_type: signal type to be set
>>+ *
>>+ * Translate pin signal type to frequency and set it on a pin.
>>+ *
>>+ * Return:
>>+ * * 0 - OK or no change required
>>+ * * negative - error
>>+ */
>>+static int
>>+__ice_dpll_pin_signal_type_set(struct ice_pf *pf, struct ice_dpll_pin
>>*pin,
>>+			       const enum ice_dpll_pin_type pin_type,
>>+			       const enum dpll_pin_signal_type sig_type)
>>+{
>>+	u8 flags;
>>+	u32 freq;
>>+	int ret;
>>+
>>+	ret = ice_dpll_pin_signal_type_to_freq(sig_type, &freq);
>>+	if (ret)
>>+		return ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>>+		ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>>+					       pin->flags, freq, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags = pin->flags | ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>>+		ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>>+						0, freq, 0);
>>+	} else {
>>+		ret = -EINVAL;
>>+	}
>>+
>>+	if (ret) {
>>+		dev_dbg(ice_pf_to_dev(pf),
>>+			"err:%d %s failed to set pin freq:%u on pin:%u\n",
>>+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>>+			freq, pin->idx);
>>+	} else {
>>+		pin->signal_type = sig_type;
>>+		pin->freq = freq;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_enable - enable a pin on dplls
>>+ * @hw: board private hw structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being enabled
>>+ *
>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		    const enum ice_dpll_pin_type pin_type)
>>+{
>>+	u8 flags = pin->flags;
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags |= ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN;
>>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		flags |= ICE_DPLL_RCLK_SOURCE_FLAG_EN;
>>+		ret = 0;
>>+	}
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+			"err:%d %s failed to enable %s pin:%u\n",
>>+			ret, ice_aq_str(hw->adminq.sq_last_status),
>>+			pin_type_name[pin_type], pin->idx);
>>+	else
>>+		pin->flags = flags;
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_disable - disable a pin on dplls
>>+ * @hw: board private hw structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being disabled
>>+ *
>>+ * Disable a pin on both dplls. Store current state in pin->flags.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		     enum ice_dpll_pin_type pin_type)
>>+{
>>+	u8 flags = pin->flags;
>>+	int ret;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		flags &= ~(ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN);
>>+		ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		flags &= ~(ICE_AQC_SET_CGU_OUT_CFG_OUT_EN);
>>+		ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		flags &= ~(ICE_DPLL_RCLK_SOURCE_FLAG_EN);
>>+		ret = ice_aq_set_phy_rec_clk_out(hw, pin->idx, false,
>>+						 &pin->freq);
>>+	}
>>+
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+			"err:%d %s failed to disable %s pin:%u\n",
>>+			ret, ice_aq_str(hw->adminq.sq_last_status),
>>+			pin_type_name[pin_type], pin->idx);
>>+	else
>>+		pin->flags = flags;
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_update - update pin's mode
>>+ * @hw: private board struct
>>+ * @pin: structure with pin attributes to be updated
>>+ * @pin_type: type of pin being updated
>>+ *
>>+ * Determine pin current mode, frequency and signal type. Then update
>>struct
>>+ * holding the pin info.
>>+ *
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+int
>>+ice_dpll_pin_update(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+		    const enum ice_dpll_pin_type pin_type)
>>+{
>>+	int ret;
>>+
>>+	pin->mode_mask = 0;
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		ret = ice_aq_get_input_pin_cfg(hw, pin->idx, NULL, NULL, NULL,
>>+					       &pin->flags, &pin->freq, NULL);
>>+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
>>+		if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags)
>>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>>+		else
>>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		ret = ice_aq_get_output_pin_cfg(hw, pin->idx, &pin->flags,
>>+						NULL, &pin->freq, NULL);
>>+		set_bit(DPLL_PIN_MODE_OUTPUT, &pin->mode_mask);
>>+		if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags)
>>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>>+		else
>>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		set_bit(DPLL_PIN_MODE_SOURCE, &pin->mode_mask);
>>+		if (ICE_DPLL_RCLK_SOURCE_FLAG_EN & pin->flags)
>>+			set_bit(DPLL_PIN_MODE_CONNECTED, &pin->mode_mask);
>>+		else
>>+			set_bit(DPLL_PIN_MODE_DISCONNECTED, &pin->mode_mask);
>>+		ret = 0;
>>+	}
>>+	pin->signal_type = ice_dpll_pin_freq_to_signal_type(pin->freq);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_mode_set - set pin's mode
>>+ * @pf: Board private structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of modified pin
>>+ * @mode: requested mode
>>+ *
>>+ * Determine requested pin mode set it on a pin.
>>+ *
>>+ * Return:
>>+ * * 0 - OK or no change required
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_mode_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+		      const enum ice_dpll_pin_type pin_type,
>>+		      const enum dpll_pin_mode mode)
>>+{
>>+	int ret;
>>+
>>+	if (!test_bit(mode, &pin->mode_supported_mask))
>>+		return -EINVAL;
>>+
>>+	if (test_bit(mode, &pin->mode_mask))
>>+		return 0;
>>+
>>+	if (mode == DPLL_PIN_MODE_CONNECTED)
>>+		ret = ice_dpll_pin_enable(&pf->hw, pin, pin_type);
>>+	else if (mode == DPLL_PIN_MODE_DISCONNECTED)
>>+		ret = ice_dpll_pin_disable(&pf->hw, pin, pin_type);
>>+	else
>>+		ret = -EINVAL;
>>+
>>+	if (!ret)
>>+		ret = ice_dpll_pin_update(&pf->hw, pin, pin_type);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_find_dpll - find ice_dpll on a pf
>>+ * @pf: private board structure
>>+ * @dpll: kernel's dpll_device pointer to be searched
>>+ *
>>+ * Return:
>>+ * * pointer if ice_dpll with given device dpll pointer is found
>>+ * * NULL if not found
>>+ */
>>+static struct ice_dpll
>>+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
>>+{
>>+	if (!pf || !dpll)
>>+		return NULL;
>>+
>>+	return (dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
>>+		dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL);
>
>Return is not a function, don't use ()'s here.

Sure, will fix.

>
>
>>+}
>>+
>>+/**
>>+ * ice_find_pin - find ice_dpll_pin on a pf
>>+ * @pf: private board structure
>>+ * @pin: kernel's dpll_pin pointer to be searched for
>>+ * @pin_type: type of pins to be searched for
>>+ *
>>+ * Find and return internal ice pin info pointer holding data of given
>>dpll subsystem
>>+ * pin pointer.
>>+ *
>>+ * Return:
>>+ * * valid 'struct ice_dpll_pin'-type pointer - if given 'pin' pointer
>>was
>>+ * found in pf internal pin data.
>>+ * * NULL - if pin was not found.
>>+ */
>>+static struct ice_dpll_pin
>>+*ice_find_pin(struct ice_pf *pf, const struct dpll_pin *pin,
>>+	      enum ice_dpll_pin_type pin_type)
>>+
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	int pin_num, i;
>>+
>>+	if (!pin || !pf)
>>+		return NULL;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		pins = pf->dplls.inputs;
>>+		pin_num = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		pins = pf->dplls.outputs;
>>+		pin_num = pf->dplls.num_outputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_RCLK_SOURCE) {
>>+		pins = pf->dplls.rclk;
>>+		pin_num = pf->dplls.num_rclk;
>>+	} else {
>>+		return NULL;
>>+	}
>>+
>>+	for (i = 0; i < pin_num; i++)
>>+		if (pin == pins[i].pin)
>>+			return &pins[i];
>>+
>>+	return NULL;
>>+}
>>+
>>+/**
>>+ * ice_dpll_hw_source_prio_set - set source priority value in hardware
>>+ * @pf: board private structure
>>+ * @dpll: ice dpll pointer
>>+ * @pin: ice pin pointer
>>+ * @prio: priority value being set on a dpll
>>+ *
>>+ * Internal wrapper for setting the priority in the hardware.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_hw_source_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
>>+			    struct ice_dpll_pin *pin, const u32 prio)
>>+{
>>+	int ret;
>>+
>>+	ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
>>+				      (u8)prio);
>>+	if (ret)
>>+		dev_dbg(ice_pf_to_dev(pf),
>>+			"err:%d %s failed to set pin prio:%u on pin:%u\n",
>>+			ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>>+			prio, pin->idx);
>>+	else
>>+		dpll->input_prio[pin->idx] = prio;
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_lock_status_get - get dpll lock status callback
>>+ * @dpll: registered dpll pointer
>>+ * @status: on success holds dpll's lock status
>>+ *
>>+ * Dpll subsystem callback, provides dpll's lock status.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_lock_status_get(const struct dpll_device *dpll,
>>+				    enum dpll_lock_status *status)
>>+{
>>+	struct ice_pf *pf = dpll_priv(dpll);
>>+	struct ice_dpll *d;
>>+
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		return -EFAULT;
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p\n", __func__, dpll,
>>pf);
>>+	mutex_lock(&pf->dplls.lock);
>>+	*status = ice_dpll_status[d->dpll_state];
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_idx_get - get dpll's source index
>>+ * @dpll: registered dpll pointer
>>+ * @pin_idx: on success holds currently selected source pin index
>>+ *
>>+ * Dpll subsystem callback. Provides index of a source dpll is trying to
>>lock
>>+ * with.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_idx_get(const struct dpll_device *dpll, u32
>>*pin_idx)
>
>Should return struct dpll_pin *. That should be the consitent driver api
>handle for pin.
>

Sure, can fix this way.

>
>>+{
>>+	struct ice_pf *pf = dpll_priv(dpll);
>>+	struct ice_dpll *d;
>>+	int ret = 0;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	*pin_idx = (u32)d->source_idx;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p d:%p, idx:%u\n",
>>+		__func__, dpll, pf, d, *pin_idx);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_mode_get - get dpll's working mode
>>+ * @dpll: registered dpll pointer
>>+ * @mode: on success holds current working mode of dpll
>>+ *
>>+ * Dpll subsystem callback. Provides working mode of dpll.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_mode_get(const struct dpll_device *dpll,
>>+			     enum dpll_mode *mode)
>>+{
>>+	struct ice_pf *pf = dpll_priv(dpll);
>>+	struct ice_dpll *d;
>>+	int ret = 0;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		ret = -EFAULT;
>>+	else
>>+		*mode = DPLL_MODE_AUTOMATIC;
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_mode_get - check if dpll's working mode is supported
>>+ * @dpll: registered dpll pointer
>>+ * @mode: mode to be checked for support
>>+ *
>>+ * Dpll subsystem callback. Provides information if working mode is
>>supported
>>+ * by dpll.
>>+ *
>>+ * Return:
>>+ * * true - mode is supported
>>+ * * false - mode is not supported
>>+ */
>>+static bool ice_dpll_mode_supported(const struct dpll_device *dpll,
>>+				    const enum dpll_mode mode)
>>+{
>>+	struct ice_pf *pf = dpll_priv(dpll);
>>+	struct ice_dpll *d;
>>+	bool ret = true;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		ret = false;
>>+	else
>>+		if (mode != DPLL_MODE_AUTOMATIC)
>
>"else if" on a single line.

Sure, will fix.

>
>
>>+			ret = false;
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_supported - if pin signal type is supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type being checked for support
>>+ * @pin_type: type of a pin being checked
>>+ *
>>+ * Check is signal type is supported on given pin/dpll pair.
>>+ *
>>+ * Return:
>>+ * * true - supported
>>+ * * false - not supported
>>+ */
>>+static bool
>>+ice_dpll_pin_signal_type_supported(const struct dpll_device *dpll,
>>+				   const struct dpll_pin *pin,
>>+				   const enum dpll_pin_signal_type sig_type,
>>+				   const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	bool supported = false;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (p) {
>>+		if (test_bit(sig_type, &p->signal_type_mask))
>
>Loose the nested if.

Sure, will fix.

>
>
>>+			supported = true;
>>+	}
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return supported;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_signal_type_supported - if rclk source signal type is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type being checked for support
>>+ *
>>+ * Dpll subsystem callback. Check is signal type is supported on given
>>pin/dpll
>>+ * pair.
>>+ *
>>+ * Return:
>>+ * * true - supported
>>+ * * false - not supported
>>+ */
>>+static bool
>>+ice_dpll_rclk_signal_type_supported(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin,
>>+				    const enum dpll_pin_signal_type sig_type)
>>+{
>>+	const enum ice_dpll_pin_type t = ICE_DPLL_PIN_TYPE_RCLK_SOURCE;
>>+
>>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type, t);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_signal_type_supported - if source signal type is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type being checked for support
>>+ *
>>+ * Dpll subsystem callback. Check is signal type is supported on given
>>pin/dpll
>>+ * pair.
>>+ *
>>+ * Return:
>>+ * * true - supported
>>+ * * false - not supported
>>+ */
>>+static bool
>>+ice_dpll_source_signal_type_supported(const struct dpll_device *dpll,
>>+				      const struct dpll_pin *pin,
>>+				      const enum dpll_pin_signal_type sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
>>+						  ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_signal_type_supported - if output pin signal type is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type being checked
>>+ *
>>+ * Dpll subsystem callback. Check if signal type is supported on given
>>in/dpll
>>+ * pair.
>>+ *
>>+ * Return:
>>+ * * true - supported
>>+ * * false - not supported
>>+ */
>>+static bool
>>+ice_dpll_output_signal_type_supported(const struct dpll_device *dpll,
>>+				      const struct dpll_pin *pin,
>>+				      const enum dpll_pin_signal_type sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_supported(dpll, pin, sig_type,
>>+						  ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_get - get dpll's pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: on success - current signal type used on the pin
>>+ * @pin_type: type of a pin being checked
>>+ *
>>+ * Find a pin and assign sig_type with its current signal type value.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_pin_signal_type_get(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					enum dpll_pin_signal_type *sig_type,
>>+					const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	int ret = -ENODEV;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (p) {
>>+		*sig_type = p->signal_type;
>>+		ret = 0;
>>+	}
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p\n",
>>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_signal_type_get - get dpll's output pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: on success - current signal type value used on the pin
>>+ *
>>+ * Dpll subsystem callback. Find current signal type of given pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_output_signal_type_get(const struct dpll_device
>>*dpll,
>>+					   const struct dpll_pin *pin,
>>+					   enum dpll_pin_signal_type *sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
>>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_signal_type_set - set dpll pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type to be set
>>+ * @pin_type: type of a pin being configured
>>+ *
>>+ * Handler for signal type modification on pins. Set signal type value
>>for
>>+ * a given pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_pin_signal_type_set(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					const enum dpll_pin_signal_type sig_type,
>>+					const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EFAULT;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p)
>>+		goto unlock;
>>+	if (test_bit(sig_type, &p->signal_type_mask))
>>+		ret = __ice_dpll_pin_signal_type_set(pf, p, pin_type,
>>sig_type);
>>+	else
>>+		ret = -EINVAL;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
>>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL,  ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_signal_type_set - set dpll output pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type to be set
>>+ *
>>+ * Dpll subsystem callback. Wraps signal type modification handler.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_output_signal_type_set(const struct dpll_device *dpll,
>>+				const struct dpll_pin *pin,
>>+				const enum dpll_pin_signal_type sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
>>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_mode_enable - enables a pin mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be set
>>+ * @pin_type: type of pin being modified
>>+ *
>>+ * Handler for enabling the pin mode.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_pin_mode_enable(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin,
>>+				    const enum dpll_pin_mode mode,
>>+				    const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EFAULT;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (p)
>>+		ret = ice_dpll_pin_mode_set(pf, p, pin_type, mode);
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s: dpll:%p, pin:%p, pf:%p, p:%p, p->pin:%p, ret:%d\n",
>>+		__func__, dpll, pin, pf, p, p ? p->pin : NULL, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_mode_enable - enable rclk-source pin mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be set
>>+ *
>>+ * Dpll subsystem callback. Enables given mode on recovered clock source
>>type
>>+ * pin.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_rclk_mode_enable(const struct dpll_device *dpll,
>>+				     const struct dpll_pin *pin,
>>+				     const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_mode_enable - enable output pin mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be set
>>+ *
>>+ * Dpll subsystem callback. Enables given mode on output type pin.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_output_mode_enable(const struct dpll_device *dpll,
>>+				       const struct dpll_pin *pin,
>>+				       const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>>+					 ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_mode_enable - enable source pin mode
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be set
>>+ *
>>+ * Dpll subsystem callback. Enables given mode on source type pin.
>>+ *
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int ice_dpll_source_mode_enable(const struct dpll_device *dpll,
>>+				       const struct dpll_pin *pin,
>>+				       const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_enable(dpll, pin, mode,
>>+					 ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_signal_type_get - get source pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @type: on success - source pin signal type
>>+ *
>>+ * Dpll subsystem callback. Get source pin signal type value.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_signal_type_get(const struct dpll_device
>>*dpll,
>>+					   const struct dpll_pin *pin,
>>+					   enum dpll_pin_signal_type *sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_get(dpll, pin, sig_type,
>>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_signal_type_set - set dpll output pin signal type
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: signal type to be set
>>+ *
>>+ * dpll subsystem callback. Set source pin signal type value.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_source_signal_type_set(const struct dpll_device *dpll,
>>+				const struct dpll_pin *pin,
>>+				const enum dpll_pin_signal_type sig_type)
>>+{
>>+	return ice_dpll_pin_signal_type_set(dpll, pin, sig_type,
>>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_prio_get - get dpll's source prio
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @prio: on success - returns source priority on dpll
>>+ *
>>+ * Dpll subsystem callback. Handler for getting priority of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_prio_get(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin, u32 *prio)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll *d = NULL;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (*prio > ICE_DPLL_PRIO_MAX)
>>+		return ret;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (!p)
>>+		goto unlock;
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		goto unlock;
>>+	*prio = d->input_prio[p->idx];
>>+	ret = 0;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>>+		__func__, dpll, pin, pf, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_prio_set - set dpll source prio
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @prio: source priority to be set on dpll
>>+ *
>>+ * Dpll subsystem callback. Handler for setting priority of a source pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_source_prio_set(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin, const u32 prio)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll *d = NULL;
>>+	struct ice_dpll_pin *p;
>>+	int ret = -EINVAL;
>>+
>>+	if (prio > ICE_DPLL_PRIO_MAX)
>>+		return ret;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (!p)
>>+		goto unlock;
>>+	d = ice_find_dpll(pf, dpll);
>>+	if (!d)
>>+		goto unlock;
>>+	ret = ice_dpll_hw_source_prio_set(pf, d, p, prio);
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>>+		__func__, dpll, pin, pf, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_mode_active -  check if given pin's mode is active
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ * @pin_type: type of a pin to be checked
>>+ *
>>+ * Handler for checking if given mode is active on a pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_pin_mode_active(const struct dpll_device *dpll,
>>+				     const struct dpll_pin *pin,
>>+				     const enum dpll_pin_mode mode,
>>+				     const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	bool ret = false;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+	if (!p)
>>+		goto unlock;
>>+	if (test_bit(mode, &p->mode_mask))
>>+		ret = true;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_mode_active - check if rclk source pin's mode is active
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>active
>>+ * on a recovered clock pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_rclk_mode_active(const struct dpll_device *dpll,
>>+				      const struct dpll_pin *pin,
>>+				      const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>>+					 ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_mode_active - check if output pin's mode is active
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>active
>>+ * on an output pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_output_mode_active(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>>+					ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_mode_active - check if source pin's mode is active
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>active
>>+ * on a source pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_source_mode_active(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_active(dpll, pin, mode,
>>+					ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_mode_supported - check if pin's mode is supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ * @pin_type: type of a pin being checked
>>+ *
>>+ * Handler for checking if given mode is supported on a pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_pin_mode_supported(const struct dpll_device *dpll,
>>+					const struct dpll_pin *pin,
>>+					const enum dpll_pin_mode mode,
>>+					const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	bool ret = false;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, pin_type);
>>+
>>+	if (!p)
>>+		goto unlock;
>>+	if (test_bit(mode, &p->mode_supported_mask))
>>+		ret = true;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_mode_supported - check if rclk pin's mode is supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>+ * supported on a clock recovery pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_rclk_mode_supported(const struct dpll_device *dpll,
>>+					 const struct dpll_pin *pin,
>>+					 const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>>+					    ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_mode_supported - check if output pin's mode is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>+ * supported on an output pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_output_mode_supported(const struct dpll_device
>>*dpll,
>>+					   const struct dpll_pin *pin,
>>+					   const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_source_mode_supported - check if source pin's mode is
>>supported
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @mode: mode to be checked
>>+ *
>>+ * DPLL subsystem callback, Wraps handler for checking if given mode is
>>+ * supported on a source pin.
>>+ *
>>+ * Return:
>>+ * * true - mode is active
>>+ * * false - mode is not active
>>+ */
>>+static bool ice_dpll_source_mode_supported(const struct dpll_device
>>*dpll,
>>+					   const struct dpll_pin *pin,
>>+					   const enum dpll_pin_mode mode)
>>+{
>>+	return ice_dpll_pin_mode_supported(dpll, pin, mode,
>>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pin_sig_type_get - get signal type of rclk pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @sig_type: on success - holds a signal type of a pin
>>+ *
>>+ * DPLL subsystem callback, provides signal type of clock recovery pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success, sig_type value is valid
>>+ * * negative - error
>>+ */
>>+static int ice_dpll_rclk_pin_sig_type_get(const struct dpll_device *dpll,
>>+					  const struct dpll_pin *pin,
>>+					  enum dpll_pin_signal_type *sig_type)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	int ret = 0;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (!p) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	*sig_type = p->signal_type;
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p sig_type:%d
>>ret:%d\n",
>>+		__func__, dpll, pin, pf, *sig_type, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pin_net_if_index_get - get OS interface index callback
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @net_if_idx: on success - holds OS interface index
>>+ *
>>+ * dpll subsystem callback, obtains OS interface index and pass to the
>>caller.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_rclk_pin_net_if_index_get(const struct dpll_device
>>*dpll,
>>+					      const struct dpll_pin *pin,
>>+					      int *net_if_idx)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+
>>+	if (!pf->vsi[0] || pf->vsi[0]->netdev)
>>+		return -EAGAIN;
>>+	*net_if_idx = pf->vsi[0]->netdev->ifindex;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pin_select - select a recovered clock pin as a valid
>>source
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ *
>>+ * dpll subsystem callback, selects a pin for clock recovery,
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_rclk_pin_select(const struct dpll_device *dpll,
>>+				    const struct dpll_pin *pin)
>>+{
>>+	struct ice_pf *pf = dpll_pin_priv(dpll, pin);
>>+	struct ice_dpll_pin *p;
>>+	u32 freq;
>>+	int ret;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	p = ice_find_pin(pf, pin, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+	if (!p) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	if (!(p->flags & ICE_DPLL_RCLK_SOURCE_FLAG_EN)) {
>>+		ret = -EPERM;
>>+		goto unlock;
>>+	}
>>+	ret = ice_aq_set_phy_rec_clk_out(&pf->hw, p->idx, true, &freq);
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>>+		__func__, dpll, pin, pf, ret);
>>+
>>+	return ret;
>>+}
>>+
>>+static struct dpll_pin_ops ice_dpll_rclk_ops = {
>>+	.mode_enable = ice_dpll_rclk_mode_enable,
>>+	.mode_active = ice_dpll_rclk_mode_active,
>>+	.mode_supported = ice_dpll_rclk_mode_supported,
>>+	.signal_type_get = ice_dpll_rclk_pin_sig_type_get,
>>+	.signal_type_supported = ice_dpll_rclk_signal_type_supported,
>>+	.net_if_idx_get = ice_dpll_rclk_pin_net_if_index_get,
>>+	.select = ice_dpll_rclk_pin_select,
>>+};
>>+
>>+static struct dpll_pin_ops ice_dpll_source_ops = {
>>+	.signal_type_get = ice_dpll_source_signal_type_get,
>>+	.signal_type_set = ice_dpll_source_signal_type_set,
>>+	.signal_type_supported = ice_dpll_source_signal_type_supported,
>>+	.mode_active = ice_dpll_source_mode_active,
>>+	.mode_enable = ice_dpll_source_mode_enable,
>>+	.mode_supported = ice_dpll_source_mode_supported,
>>+	.prio_get = ice_dpll_source_prio_get,
>>+	.prio_set = ice_dpll_source_prio_set,
>>+};
>>+
>>+static struct dpll_pin_ops ice_dpll_output_ops = {
>>+	.signal_type_get = ice_dpll_output_signal_type_get,
>>+	.signal_type_set = ice_dpll_output_signal_type_set,
>>+	.signal_type_supported = ice_dpll_output_signal_type_supported,
>>+	.mode_active = ice_dpll_output_mode_active,
>>+	.mode_enable = ice_dpll_output_mode_enable,
>>+	.mode_supported = ice_dpll_output_mode_supported,
>>+};
>>+
>>+static struct dpll_device_ops ice_dpll_ops = {
>>+	.lock_status_get = ice_dpll_lock_status_get,
>>+	.source_pin_idx_get = ice_dpll_source_idx_get,
>>+	.mode_get = ice_dpll_mode_get,
>>+	.mode_supported = ice_dpll_mode_supported,
>>+};
>>+
>>+/**
>>+ * ice_dpll_release_info - release memory allocated for pins
>>+ * @pf: board private structure
>>+ *
>>+ * Release memory allocated for pins by ice_dpll_init_info function.
>>+ */
>>+static void ice_dpll_release_info(struct ice_pf *pf)
>>+{
>>+	kfree(pf->dplls.inputs);
>>+	pf->dplls.inputs = NULL;
>>+	kfree(pf->dplls.outputs);
>>+	pf->dplls.outputs = NULL;
>>+	kfree(pf->dplls.eec.input_prio);
>>+	pf->dplls.eec.input_prio = NULL;
>>+	kfree(pf->dplls.pps.input_prio);
>>+	pf->dplls.pps.input_prio = NULL;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins - initializes source or output pins information
>>+ * @pf: Board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Init information about input or output pins, cache them in pins
>>struct.
>>+ */
>>+static int ice_dpll_init_pins(struct ice_pf *pf,
>>+			      const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>>+	int ret = -EINVAL, num_pins, i;
>>+	struct ice_hw *hw = &pf->hw;
>>+	struct ice_dpll_pin *pins;
>>+	bool input;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		input = true;
>>+		pins = pf->dplls.inputs;
>>+		num_pins = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		input = false;
>>+		pins = pf->dplls.outputs;
>>+		num_pins = pf->dplls.num_outputs;
>>+	} else {
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < num_pins; i++) {
>>+		pins[i].idx = i;
>>+		pins[i].name = ice_cgu_get_pin_name(hw, i, input);
>>+		pins[i].type = ice_cgu_get_pin_type(hw, i, input);
>>+		set_bit(DPLL_PIN_MODE_CONNECTED,
>>+			&pins[i].mode_supported_mask);
>>+		set_bit(DPLL_PIN_MODE_DISCONNECTED,
>>+			&pins[i].mode_supported_mask);
>>+		if (input) {
>>+			ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>>+						      &de->input_prio[i]);
>>+			if (ret)
>>+				return ret;
>>+			ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>>+						      &dp->input_prio[i]);
>>+			if (ret)
>>+				return ret;
>>+			set_bit(DPLL_PIN_MODE_SOURCE,
>>+				&pins[i].mode_supported_mask);
>>+		} else {
>>+			set_bit(DPLL_PIN_MODE_OUTPUT,
>>+				&pins[i].mode_supported_mask);
>>+		}
>>+		pins[i].signal_type_mask =
>>+				ice_cgu_get_pin_sig_type_mask(hw, i, input);
>>+		ret = ice_dpll_pin_update(hw, &pins[i], pin_type);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_release_pins - release pin's from dplls registered in
>>subsystem
>>+ * @dpll_eec: dpll_eec dpll pointer
>>+ * @dpll_pps: dpll_pps dpll pointer
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ *
>>+ * Deregister and free pins of a given array of pins from dpll devices
>>registered
>>+ * in dpll subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * positive - number of errors encounterd on pin's deregistration.
>>+ */
>>+static int
>>+ice_dpll_release_pins(struct dpll_device *dpll_eec,
>>+		      struct dpll_device *dpll_pps, struct ice_dpll_pin *pins,
>>+		      int count)
>>+{
>>+	int i, ret, err;
>>+
>>+	for (i = 0; i < count; i++) {
>>+		struct ice_dpll_pin *p = &pins[i];
>>+
>>+		if (p && p->pin) {
>>+			if (dpll_eec) {
>>+				ret = dpll_pin_deregister(dpll_eec, p->pin);
>>+				if (ret)
>>+					err++;
>>+			}
>>+			if (dpll_pps) {
>>+				ret = dpll_pin_deregister(dpll_pps, p->pin);
>>+				if (ret)
>>+					err++;
>>+			}
>>+			dpll_pin_free(p->pin);
>>+			p->pin = NULL;
>>+		}
>>+	}
>>+
>>+	return err;
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_pins - register pins with a dpll
>>+ * @pf: board private structure
>>+ * @dpll: registered dpll pointer
>>+ * @pin_type: type of pins being registered
>>+ *
>>+ * Register source or output pins within given DPLL in a Linux dpll
>>subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_register_pins(struct ice_pf *pf, struct dpll_device *dpll,
>>+		       const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	struct dpll_pin_ops *ops;
>>+	int ret, i, count;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		ops = &ice_dpll_source_ops;
>>+		pins = pf->dplls.inputs;
>>+		count = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		ops = &ice_dpll_output_ops;
>>+		pins = pf->dplls.outputs;
>>+		count = pf->dplls.num_outputs;
>>+	} else {
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < count; i++) {
>>+		pins[i].pin = dpll_pin_alloc(pins[i].name, pins[i].type);
>>+		if (IS_ERR_OR_NULL(pins[i].pin))
>>+			return -ENOMEM;
>>+
>>+		ret = dpll_pin_register(dpll, pins[i].pin, ops, pf);
>>+		if (ret)
>>+			return -ENOSPC;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_shared_pins - register shared pins in DPLL subsystem
>>+ * @pf: board private structure
>>+ * @dpll_o: registered dpll pointer (owner)
>>+ * @dpll: registered dpll pointer
>>+ * @type: type of pins being registered
>>+ *
>>+ * Register pins from given owner dpll within given dpll in Linux dpll
>>subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_register_shared_pins(struct ice_pf *pf, struct dpll_device
>>*dpll_o,
>>+			      struct dpll_device *dpll,
>>+			      const enum ice_dpll_pin_type pin_type)
>>+{
>>+	struct ice_dpll_pin *pins;
>>+	struct dpll_pin_ops *ops;
>>+	int ret, i, count;
>>+
>>+	if (pin_type == ICE_DPLL_PIN_TYPE_SOURCE) {
>>+		ops = &ice_dpll_source_ops;
>>+		pins = pf->dplls.inputs;
>>+		count = pf->dplls.num_inputs;
>>+	} else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT) {
>>+		ops = &ice_dpll_output_ops;
>>+		pins = pf->dplls.outputs;
>>+		count = pf->dplls.num_outputs;
>>+	} else {
>>+		return -EINVAL;
>>+	}
>>+
>>+	for (i = 0; i < count; i++) {
>>+		ret = dpll_shared_pin_register(dpll_o, dpll, pins[i].name,
>>+					       ops, pf);
>>+		if (ret)
>>+			return ret;
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_info - prepare pf's dpll information structure
>>+ * @pf: board private structure
>>+ *
>>+ * Acquire (from HW) and set basic dpll information (on pf->dplls
>>struct).
>>+ *
>>+ * Return:
>>+ *  0 - success
>>+ *  negative - error
>>+ */
>>+static int ice_dpll_init_info(struct ice_pf *pf)
>>+{
>>+	struct ice_aqc_get_cgu_abilities abilities;
>>+	struct ice_dpll *de = &pf->dplls.eec;
>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_hw *hw = &pf->hw;
>>+	int ret, alloc_size;
>>+
>>+	ret = ice_aq_get_cgu_abilities(hw, &abilities);
>>+	if (ret) {
>>+		dev_err(ice_pf_to_dev(pf),
>>+			"err:%d %s failed to read cgu abilities\n",
>>+			ret, ice_aq_str(hw->adminq.sq_last_status));
>>+		return ret;
>>+	}
>>+
>>+	de->dpll_idx = abilities.eec_dpll_idx;
>>+	dp->dpll_idx = abilities.pps_dpll_idx;
>>+	d->num_inputs = abilities.num_inputs;
>>+	alloc_size = sizeof(*d->inputs) * d->num_inputs;
>>+	d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!d->inputs)
>>+		return -ENOMEM;
>>+
>>+	alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>>+	de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!de->input_prio)
>>+		return -ENOMEM;
>>+
>>+	dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!dp->input_prio)
>>+		return -ENOMEM;
>>+
>>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (ret)
>>+		goto release_info;
>>+
>>+	d->num_outputs = abilities.num_outputs;
>>+	alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>+	d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>+	if (!d->outputs)
>>+		goto release_info;
>>+
>>+	ret = ice_dpll_init_pins(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+	if (ret)
>>+		goto release_info;
>>+
>>+	dev_dbg(ice_pf_to_dev(pf), "%s - success, inputs:%u, outputs:%u\n",
>>__func__,
>>+		abilities.num_inputs, abilities.num_outputs);
>>+
>>+	return 0;
>>+
>>+release_info:
>>+	dev_err(ice_pf_to_dev(pf),
>>+		"%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p,
>>d->outputs:%p\n",
>>+		__func__, d->inputs, de->input_prio,
>>+		dp->input_prio, d->outputs);
>>+	ice_dpll_release_info(pf);
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_generate_clock_id - generates unique clock_id for registering
>>dpll.
>>+ * @pf: board private structure
>>+ * @clock_id: holds generated clock_id
>>+ *
>>+ * Generates unique (per board) clock_id for allocation and search of
>>dpll
>>+ * devices in Linux dpll subsystem.
>>+ */
>>+static void ice_generate_clock_id(struct ice_pf *pf, u64 *clock_id)
>>+{
>>+	*clock_id = pci_get_dsn(pf->pdev);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_dpll
>>+ * @pf: board private structure
>>+ *
>>+ * Allocate and register dpll in dpll subsystem.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - allocation fails
>>+ */
>>+static int ice_dpll_init_dpll(struct ice_pf *pf)
>>+{
>>+	struct device *dev = ice_pf_to_dev(pf);
>>+	struct ice_dpll *de = &pf->dplls.eec;
>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>+	u64 clock_id = 0;
>>+	int ret = 0;
>>+
>>+	ice_generate_clock_id(pf, &clock_id);
>>+
>>+	de->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_EEC,
>>+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
>>+	if (!de->dpll) {
>>+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (eec)\n");
>>+		return -ENOMEM;
>>+	}
>>+
>>+	dp->dpll = dpll_device_alloc(&ice_dpll_ops, DPLL_TYPE_PPS,
>>+				     clock_id, DPLL_CLOCK_CLASS_C, 0, pf, dev);
>>+	if (!dp->dpll) {
>>+		dev_err(ice_pf_to_dev(pf), "dpll_device_alloc failed (pps)\n");
>>+		return -ENOMEM;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_update_state
>>+ * @hw: board private structure
>>+ * @d: pointer to queried dpll device
>>+ *
>>+ * Poll current state of dpll from hw and update ice_dpll struct.
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - AQ failure
>>+ */
>>+static int ice_dpll_update_state(struct ice_hw *hw, struct ice_dpll *d)
>>+{
>>+	int ret;
>>+
>>+	ret = ice_get_cgu_state(hw, d->dpll_idx, d->prev_dpll_state,
>>+				&d->source_idx, &d->ref_state, &d->eec_mode,
>>+				&d->phase_offset, &d->dpll_state);
>>+
>>+	dev_dbg(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+		"update dpll=%d, src_idx:%u, state:%d, prev:%d\n",
>>+		d->dpll_idx, d->source_idx,
>>+		d->dpll_state, d->prev_dpll_state);
>>+
>>+	if (ret)
>>+		dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+			"update dpll=%d state failed, ret=%d %s\n",
>>+			d->dpll_idx, ret,
>>+			ice_aq_str(hw->adminq.sq_last_status));
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_notify_changes - notify dpll subsystem about changes
>>+ * @d: pointer do dpll
>>+ *
>>+ * Once change detected appropriate event is submitted to the dpll
>>subsystem.
>>+ */
>>+static void ice_dpll_notify_changes(struct ice_dpll *d)
>>+{
>>+	if (d->prev_dpll_state != d->dpll_state) {
>>+		d->prev_dpll_state = d->dpll_state;
>>+		dpll_device_notify(d->dpll, DPLL_CHANGE_LOCK_STATUS);
>>+	}
>>+	if (d->prev_source_idx != d->source_idx) {
>>+		d->prev_source_idx = d->source_idx;
>>+		dpll_device_notify(d->dpll, DPLL_CHANGE_SOURCE_PIN);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>+ * @work: pointer to kthread_work structure
>>+ *
>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>+ */
>>+static void ice_dpll_periodic_work(struct kthread_work *work)
>>+{
>>+	struct ice_dplls *d = container_of(work, struct ice_dplls,
>>work.work);
>>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>>+	struct ice_dpll *de = &pf->dplls.eec;
>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>+	int ret = 0;
>>+
>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>
>Why do you need to check the flag there, this would should not be
>ever scheduled in case the flag was not set.
>

It's here rather for stopping the worker, It shall no longer reschedule and
bail out.

>
>>+		return;
>>+	mutex_lock(&d->lock);
>>+	ret = ice_dpll_update_state(&pf->hw, de);
>>+	if (!ret)
>>+		ret = ice_dpll_update_state(&pf->hw, dp);
>>+	if (ret) {
>>+		d->cgu_state_acq_err_num++;
>>+		/* stop rescheduling this worker */
>>+		if (d->cgu_state_acq_err_num >
>>+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
>>+			dev_err(ice_pf_to_dev(pf),
>>+				"EEC/PPS DPLLs periodic work disabled\n");
>>+			return;
>>+		}
>>+	}
>>+	mutex_unlock(&d->lock);
>>+	ice_dpll_notify_changes(de);
>>+	ice_dpll_notify_changes(dp);
>>+
>>+	/* Run twice a second or reschedule if update failed */
>>+	kthread_queue_delayed_work(d->kworker, &d->work,
>>+				   ret ? msecs_to_jiffies(10) :
>>+				   msecs_to_jiffies(500));
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>>+ * @pf: board private structure
>>+ *
>>+ * Create and start DPLLs periodic worker.
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - create worker failure
>>+ */
>>+static int ice_dpll_init_worker(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct kthread_worker *kworker;
>>+
>>+	ice_dpll_update_state(&pf->hw, &d->eec);
>>+	ice_dpll_update_state(&pf->hw, &d->pps);
>>+	kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>>+	kworker = kthread_create_worker(0, "ice-dplls-%s",
>>+					dev_name(ice_pf_to_dev(pf)));
>>+	if (IS_ERR(kworker))
>>+		return PTR_ERR(kworker);
>>+	d->kworker = kworker;
>>+	d->cgu_state_acq_err_num = 0;
>>+	kthread_queue_delayed_work(d->kworker, &d->work, 0);
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * __ice_dpll_release - Disable the driver/HW support for DPLL and
>>unregister
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * This function handles the cleanup work required from the
>>initialization by
>>+ * freeing resources and unregistering the dpll.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void __ice_dpll_release(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+	struct ice_dpll *de = &d->eec;
>>+	struct ice_dpll *dp = &d->pps;
>>+	int ret;
>>+
>>+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->inputs,
>>+				    d->num_inputs);
>>+	if (ret)
>>+		dev_warn(ice_pf_to_dev(pf),
>>+			 "pin deregister on PPS dpll err=%d\n", ret);
>>+	ret = ice_dpll_release_pins(de->dpll, dp->dpll, d->outputs,
>>+				    d->num_outputs);
>>+	if (ret)
>>+		dev_warn(ice_pf_to_dev(pf),
>>+			 "pin deregister on PPS dpll err=%d\n", ret);
>>+	ice_dpll_release_info(pf);
>>+	if (dp->dpll) {
>>+		dpll_device_unregister(dp->dpll);
>>+		dpll_device_free(dp->dpll);
>>+		dev_dbg(ice_pf_to_dev(pf), "PPS dpll removed\n");
>>+	}
>>+
>>+	if (de->dpll) {
>>+		dpll_device_unregister(de->dpll);
>>+		dpll_device_free(de->dpll);
>>+		dev_dbg(ice_pf_to_dev(pf), "EEC dpll removed\n");
>>+	}
>>+
>>+	kthread_cancel_delayed_work_sync(&d->work);
>>+	if (d->kworker) {
>>+		kthread_destroy_worker(d->kworker);
>>+		d->kworker = NULL;
>>+		dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_init - Initialize DPLLs support
>>+ * @pf: board private structure
>>+ *
>>+ * Set up the device as owner of DPLLs registering them and pins
>>connected
>>+ * within Linux dpll subsystem. Allow userpsace to obtain state of DPLL
>>+ * and handling of DPLL configuration requests.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+int ice_dpll_init(struct ice_pf *pf)
>>+{
>>+	struct ice_dplls *d = &pf->dplls;
>>+	int err;
>>+
>>+	mutex_init(&d->lock);
>>+	mutex_lock(&d->lock);
>
>It is always odd to see the lock being created and locked right away.
>Why do you need to lock it here?
>

Once dpll is registered it can start sending requests, we want to have fully
initialized dpll/pins before that. Lock could be done after ice_dpll_init_info.

>
>>+	err = ice_dpll_init_info(pf);
>>+	if (err)
>>+		goto unlock;
>>+	err = ice_dpll_init_dpll(pf);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_pins(pf, d->eec.dpll,
>>ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_pins(pf, d->eec.dpll,
>>ICE_DPLL_PIN_TYPE_OUTPUT);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
>>+					    ICE_DPLL_PIN_TYPE_SOURCE);
>>+	if (err)
>>+		goto release;
>>+	err = ice_dpll_register_shared_pins(pf, d->eec.dpll, d->pps.dpll,
>>+					    ICE_DPLL_PIN_TYPE_OUTPUT);
>>+	if (err)
>>+		goto release;
>>+	set_bit(ICE_FLAG_DPLL, pf->flags);
>>+	err = ice_dpll_init_worker(pf);
>>+	if (err)
>>+		goto release;
>>+	mutex_unlock(&d->lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "DPLLs init successful\n");
>>+
>>+	return err;
>>+release:
>>+	__ice_dpll_release(pf);
>>+unlock:
>>+	clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+	mutex_unlock(&d->lock);
>>+	mutex_destroy(&d->lock);
>>+	dev_warn(ice_pf_to_dev(pf), "DPLLs init failure\n");
>>+
>>+	return err;
>>+}
>>+
>>+/**
>>+ * ice_dpll_release - Disable the driver/HW support for DPLLs and
>>unregister
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * This function handles the cleanup work required from the
>>initialization by
>>+ * freeing resources and unregistering the dpll.
>>+ */
>>+void ice_dpll_release(struct ice_pf *pf)
>>+{
>>+	if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>+		mutex_lock(&pf->dplls.lock);
>>+		clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+		__ice_dpll_release(pf);
>>+		mutex_unlock(&pf->dplls.lock);
>>+		mutex_destroy(&pf->dplls.lock);
>>+	}
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pin_init - init the pin info for recovered clock
>>+ * @attr: structure with pin attributes
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+void ice_dpll_rclk_pin_init(struct ice_dpll_pin *p)
>>+{
>>+	p->flags = ICE_DPLL_RCLK_SOURCE_FLAG_EN;
>>+	p->type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>>+	set_bit(DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ, &p->signal_type_mask);
>>+	set_bit(DPLL_PIN_MODE_CONNECTED,	  &p->mode_supported_mask);
>>+	set_bit(DPLL_PIN_MODE_DISCONNECTED,	  &p->mode_supported_mask);
>>+	set_bit(DPLL_PIN_MODE_SOURCE,		  &p->mode_supported_mask);
>>+	ice_dpll_pin_update(0, p, ICE_DPLL_PIN_TYPE_RCLK_SOURCE);
>>+}
>>+
>>+/**
>>+ * __ice_dpll_rclk_release - unregister the recovered pin for dpll device
>>+ * @pf: board private structure
>>+ *
>>+ * This function handles the cleanup work required from the
>>initialization by
>>+ * freeing resources and unregistering the recovered pin.
>>+ */
>>+void __ice_dpll_rclk_release(struct ice_pf *pf)
>>+{
>>+	int ret = 0;
>>+
>>+	if (pf->dplls.eec.dpll) {
>>+		if (pf->dplls.rclk[0].pin)
>>+			ret = dpll_pin_deregister(pf->dplls.eec.dpll,
>>+						  pf->dplls.rclk[0].pin);
>>+		dpll_pin_free(pf->dplls.rclk->pin);
>>+		kfree(pf->dplls.rclk);
>>+	}
>>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_pins_init - init the pin for recovered clock
>>+ * @pf: board private structure
>>+ * @first_parent: pointer to a first parent pin
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+int ice_dpll_rclk_pins_init(struct ice_pf *pf, struct ice_dpll_pin
>>*first_parent)
>>+{
>>+	struct ice_dpll_pin *parent, *p;
>>+	char *name;
>>+	int i, ret;
>>+
>>+	if (pf->dplls.rclk)
>>+		return -EEXIST;
>>+	pf->dplls.rclk = kcalloc(pf->dplls.num_rclk, sizeof(*pf->dplls.rclk),
>>+				 GFP_KERNEL);
>>+	if (!pf->dplls.rclk)
>>+		goto release;
>>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>>+		p = &pf->dplls.rclk[i];
>>+		if (!p)
>>+			goto release;
>>+		ice_dpll_rclk_pin_init(p);
>>+		parent = first_parent + i;
>>+		if (!parent)
>>+			goto release;
>>+		p->idx = i;
>>+		name = kcalloc(DPLL_PIN_DESC_LEN, sizeof(*p->name),
>>GFP_KERNEL);
>>+		if (!name)
>>+			goto release;
>>+		snprintf(name, DPLL_PIN_DESC_LEN - 1, "%s-%u",
>>+			 parent->name, pf->hw.pf_id);
>>+		p->name = name;
>>+		p->pin = dpll_pin_alloc(p->name, p->type);
>>+		if (IS_ERR_OR_NULL(p->pin))
>>+			goto release;
>>+		ret = dpll_muxed_pin_register(pf->dplls.eec.dpll, parent->name,
>>+					      p->pin, &ice_dpll_rclk_ops, pf);
>>+		if (ret)
>>+			goto release;
>>+		ret = dpll_shared_pin_register(pf->dplls.eec.dpll,
>>+					       pf->dplls.pps.dpll,
>>+					       p->name,
>>+					       &ice_dpll_rclk_ops, pf);
>>+		if (ret)
>>+			goto release;
>>+	}
>>+
>>+	return ret;
>>+release:
>>+	dev_dbg(ice_pf_to_dev(pf),
>>+		"%s releasing - p: %p, parent:%p, p->pin:%p name:%s, ret:%d\n",
>>+		__func__, p, parent, p->pin, name, ret);
>>+	__ice_dpll_rclk_release(pf);
>>+	return -ENOMEM;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
>>+ * @pf: board private structure
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf)
>>+{
>>+	u64 clock_id = 0;
>>+
>>+	ice_generate_clock_id(pf, &clock_id);
>>+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,
>
>I have to say I'm a bit lost in this code. Why exactly do you need this
>here? Looks like the pointer was set in ice_dpll_init_dpll().
>
>Or, is that in case of a different PF instantiating the DPLL instances?

Yes it is, different PF is attaching recovered clock pins with this.

>If yes, I'm pretty sure what it is wrong. What is the PF which did
>instanticate those unbinds? You have to share the dpll instance,
>refcount it.
>

It will break, as in our case only one designated PF controls the dpll.

>Btw, you have a problem during init as well, as the order matters. What
>if the other function probes only after executing this? You got -EFAULT
>here and bail out.
>

We don't expect such use case, altough I see your point, will try to fix it.

>In mlx5, I also share one dpll instance between 2 PFs. What I do is I
>create mlx5-dpll instance which is refcounted, created by first probed
>PF and removed by the last one. In mlx5 case, the PFs are equal, nobody
>is an owner of the dpll. In your case, I think it is different. So
>probably better to implement the logic in driver then in the dpll core.
>

For this NIC only one PF will control the dpll, so there is a designated owner.
Here owner must not only initialize a dpll but also register its pin,
so the other PFs could register additional pins. Basically it means
for ice that we can only rely on some postponed rclk initialization for
a case of unordered PF initialization. Seems doable.

>Then you don't need dpll_device_get_by_clock_id at all. If you decide to
>implement that in dpll core, I believe that there should be some
>functions like:
>dpll = dpll_device_get(ops, clock_id, ...)  - to create/get reference
>dpll_device_put(dpll)                       - to put reference/destroy

Sure, we can rename "dpll_device_get_by_clock_id" to "dpll_device_get" (as
it is only function currently exported for such behavior), and add
"dpll_device_put", with ref counts as suggested.

>
>First caller of dpll_device_get() actually makes dpll to instantiate the
>device.
>

Maybe I am missing something.. do you suggest that "dpll_device_get" would
allocate dpll device and do ref count, and then dpll_device_register(..) call
would assign all the arguments that are available only in the owner instance?
Or i got it wrong?

>
>
>>+							 DPLL_TYPE_EEC, 0);
>>+	if (!pf->dplls.eec.dpll)
>>+		return -EFAULT;
>>+	pf->dplls.pps.dpll = dpll_device_get_by_clock_id(clock_id,
>>+							 DPLL_TYPE_PPS, 0);
>>+	if (!pf->dplls.pps.dpll)
>>+		return -EFAULT;
>>+
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_parent_pins_init - initialize the recovered clock parent
>>pins
>>+ * @pf: board private structure
>>+ * @base_rclk_idx: number of first recovered clock pin in DPLL
>>+ *
>>+ * This function shall be executed only if ICE_FLAG_DPLL feature is not
>
>Feature? It's a flag.
>

Sure, will fix.

Thanks,
Arkadiusz

>
>>+ * supported.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+static int ice_dpll_rclk_parent_pins_init(struct ice_pf *pf, u8
>>base_rclk_idx)
>>+{
>>+	int i;
>>+
>>+	if (pf->dplls.inputs)
>>+		return -EINVAL;
>>+	pf->dplls.inputs = kcalloc(pf->dplls.num_rclk,
>>+				   sizeof(*pf->dplls.inputs), GFP_KERNEL);
>>+
>>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>>+		const char *desc;
>>+
>>+		desc = ice_cgu_get_pin_name(&pf->hw, base_rclk_idx + i, true);
>>+		if (!desc)
>>+			return -EINVAL;
>>+		pf->dplls.inputs[i].name = desc;
>>+	}
>>+	return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_init - Enable support for DPLL's PHY clock recovery
>>+ * @pf: board private structure
>>+ *
>>+ * Context:
>>+ * Acquires a pf->dplls.lock. If PF is not an owner of DPLL it shall find
>>and
>>+ * connect its pins with the device dpll.
>>+ *
>>+ * This function handles enablement of PHY clock recovery part for
>>timesync
>>+ * capabilities.
>>+ * Prepares and initalizes resources required to register its PHY clock
>sources
>>+ * within DPLL subsystem.
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure
>>+ */
>>+int ice_dpll_rclk_init(struct ice_pf *pf)
>>+{
>>+	struct ice_dpll_pin *first_parent = NULL;
>>+	u8 base_rclk_idx;
>>+	int ret;
>>+
>>+	ret = ice_get_cgu_rclk_pin_info(&pf->hw, &base_rclk_idx,
>>+					&pf->dplls.num_rclk);
>>+	if (ret)
>>+		return ret;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>+		ret = ice_dpll_rclk_find_dplls(pf);
>>+		dev_dbg(ice_pf_to_dev(pf), "ecc:%p, pps:%p\n",
>>+			pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>>+		if (ret)
>>+			goto unlock;
>>+		ret = ice_dpll_rclk_parent_pins_init(pf, base_rclk_idx);
>>+		if (ret)
>>+			goto unlock;
>>+		first_parent = &pf->dplls.inputs[0];
>>+	} else {
>>+		first_parent = &pf->dplls.inputs[base_rclk_idx];
>>+	}
>>+	if (!first_parent) {
>>+		ret = -EFAULT;
>>+		goto unlock;
>>+	}
>>+	ret = ice_dpll_rclk_pins_init(pf, first_parent);
>>+unlock:
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK init ret=%d\n", ret);
>>+
>>+	return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_release - Disable the support for DPLL's PHY clock
>>recovery
>>+ * @pf: board private structure
>>+ *
>>+ * Context:
>>+ * Acquires a pf->dplls.lock. Requires dplls to be present, must be
>called
>>+ * before dplls are realesed.
>>+ *
>>+ * This function handles the cleanup work of resources allocated for
>>enablement
>>+ * of PHY recovery clock mechanics.
>>+ * Unregisters RCLK pins and frees pin's memory allocated by
>>ice_dpll_rclk_init.
>>+ */
>>+void ice_dpll_rclk_release(struct ice_pf *pf)
>>+{
>>+	int i, ret = 0;
>>+
>>+	if (!pf->dplls.rclk)
>>+		return;
>>+
>>+	mutex_lock(&pf->dplls.lock);
>>+	for (i = ICE_RCLKA_PIN; i < pf->dplls.num_rclk; i++) {
>>+		if (pf->dplls.rclk[i].pin) {
>>+			dpll_pin_deregister(pf->dplls.eec.dpll,
>>+					    pf->dplls.rclk[i].pin);
>>+			dpll_pin_deregister(pf->dplls.pps.dpll,
>>+					    pf->dplls.rclk[i].pin);
>>+			dpll_pin_free(pf->dplls.rclk[i].pin);
>>+			pf->dplls.rclk[i].pin = NULL;
>>+		}
>>+		kfree(pf->dplls.rclk[i].name);
>>+		pf->dplls.rclk[i].name = NULL;
>>+	}
>>+	/* inputs were prepared only for RCLK, release them here */
>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags)) {
>>+		kfree(pf->dplls.inputs);
>>+		pf->dplls.inputs = NULL;
>>+	}
>>+	kfree(pf->dplls.rclk);
>>+	pf->dplls.rclk = NULL;
>>+	mutex_unlock(&pf->dplls.lock);
>>+	dev_dbg(ice_pf_to_dev(pf), "PHY RCLK release ret:%d\n", ret);
>>+}
>>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h
>>b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>new file mode 100644
>>index 000000000000..3390d60f2fab
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>@@ -0,0 +1,99 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/* Copyright (C) 2022, Intel Corporation. */
>>+
>>+#ifndef _ICE_DPLL_H_
>>+#define _ICE_DPLL_H_
>>+
>>+#include "ice.h"
>>+
>>+#define ICE_DPLL_PRIO_MAX	0xF
>>+
>>+/** ice_dpll_pin - store info about pins
>>+ * @pin: dpll pin structure
>>+ * @flags: pin flags returned from HW
>>+ * @idx: ice pin private idx
>>+ * @type: type of a pin
>>+ * @signal_type: current signal type
>>+ * @signal_type_mask: signal types supported
>>+ * @freq: current frequency of a pin
>>+ * @mode_mask: current pin modes as bitmask
>>+ * @mode_supported_mask: supported pin modes
>>+ * @name: pin name
>>+ */
>>+struct ice_dpll_pin {
>>+	struct dpll_pin *pin;
>>+#define ICE_DPLL_RCLK_SOURCE_FLAG_EN	BIT(0)
>>+	u8 flags;
>>+	u8 idx;
>>+	enum dpll_pin_type type;
>>+	enum dpll_pin_signal_type signal_type;
>>+	unsigned long signal_type_mask;
>>+	u32 freq;
>>+	unsigned long mode_mask;
>>+	unsigned long mode_supported_mask;
>>+	const char *name;
>>+};
>>+
>>+/** ice_dpll - store info required for DPLL control
>>+ * @dpll: pointer to dpll dev
>>+ * @dpll_idx: index of dpll on the NIC
>>+ * @source_idx: source currently selected
>>+ * @prev_source_idx: source previously selected
>>+ * @ref_state: state of dpll reference signals
>>+ * @eec_mode: eec_mode dpll is configured for
>>+ * @phase_offset: phase delay of a dpll
>>+ * @input_prio: priorities of each input
>>+ * @dpll_state: current dpll sync state
>>+ * @prev_dpll_state: last dpll sync state
>>+ */
>>+struct ice_dpll {
>>+	struct dpll_device *dpll;
>>+	int dpll_idx;
>>+	u8 source_idx;
>>+	u8 prev_source_idx;
>>+	u8 ref_state;
>>+	u8 eec_mode;
>>+	s64 phase_offset;
>>+	u8 *input_prio;
>>+	enum ice_cgu_state dpll_state;
>>+	enum ice_cgu_state prev_dpll_state;
>>+};
>>+
>>+/** ice_dplls - store info required for CCU (clock controlling unit)
>>+ * @kworker: periodic worker
>>+ * @work: periodic work
>>+ * @lock: locks access to configuration of a dpll
>>+ * @eec: pointer to EEC dpll dev
>>+ * @pps: pointer to PPS dpll dev
>>+ * @inputs: input pins pointer
>>+ * @outputs: output pins pointer
>>+ * @rclk: recovered pins pointer
>>+ * @num_inputs: number of input pins available on dpll
>>+ * @num_outputs: number of output pins available on dpll
>>+ * @num_rclk: number of recovered clock pins available on dpll
>>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>>+ */
>>+struct ice_dplls {
>>+	struct kthread_worker *kworker;
>>+	struct kthread_delayed_work work;
>>+	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 *rclk;
>>+	u32 num_inputs;
>>+	u32 num_outputs;
>>+	u8 num_rclk;
>>+	int cgu_state_acq_err_num;
>>+};
>>+
>>+int ice_dpll_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_release(struct ice_pf *pf);
>>+
>>+int ice_dpll_rclk_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_rclk_release(struct ice_pf *pf);
>>+
>>+#endif
>>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c
>>b/drivers/net/ethernet/intel/ice/ice_main.c
>>index a9a7f8b52140..8b65f4ad245e 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>>@@ -4896,6 +4896,12 @@ ice_probe(struct pci_dev *pdev, const struct
>>pci_device_id __always_unused *ent)
>> 	if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
>> 		ice_ptp_init(pf);
>>
>>+	if (ice_is_feature_supported(pf, ICE_F_CGU))
>>+		ice_dpll_init(pf);
>>+
>>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>>+		ice_dpll_rclk_init(pf);
>>+
>> 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
>> 		ice_gnss_init(pf);
>>
>>@@ -5078,6 +5084,10 @@ static void ice_remove(struct pci_dev *pdev)
>> 		ice_ptp_release(pf);
>> 	if (ice_is_feature_supported(pf, ICE_F_GNSS))
>> 		ice_gnss_exit(pf);
>>+	if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>>+		ice_dpll_rclk_release(pf);
>>+	if (ice_is_feature_supported(pf, ICE_F_CGU))
>>+		ice_dpll_release(pf);
>> 	if (!ice_is_safe_mode(pf))
>> 		ice_remove_arfs(pf);
>> 	ice_setup_mc_magic_wake(pf);
>>--
>>2.30.2
>>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
  2023-01-27 18:13       ` Kubalewski, Arkadiusz
@ 2023-01-31 13:00         ` Jiri Pirko
  -1 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-31 13:00 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

Fri, Jan 27, 2023 at 07:13:20PM CET, arkadiusz.kubalewski@intel.com wrote:
>
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, January 19, 2023 3:54 PM
>>
>>Tue, Jan 17, 2023 at 07:00:51PM CET, vadfed@meta.com wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]


>>>+/**
>>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>>+ * @work: pointer to kthread_work structure
>>>+ *
>>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>>+ */
>>>+static void ice_dpll_periodic_work(struct kthread_work *work)
>>>+{
>>>+	struct ice_dplls *d = container_of(work, struct ice_dplls,
>>>work.work);
>>>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>>>+	struct ice_dpll *de = &pf->dplls.eec;
>>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>>+	int ret = 0;
>>>+
>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>
>>Why do you need to check the flag there, this would should not be
>>ever scheduled in case the flag was not set.
>>
>
>It's here rather for stopping the worker, It shall no longer reschedule and
>bail out.

How that can happen?



>
>>
>>>+		return;
>>>+	mutex_lock(&d->lock);
>>>+	ret = ice_dpll_update_state(&pf->hw, de);
>>>+	if (!ret)
>>>+		ret = ice_dpll_update_state(&pf->hw, dp);
>>>+	if (ret) {
>>>+		d->cgu_state_acq_err_num++;
>>>+		/* stop rescheduling this worker */
>>>+		if (d->cgu_state_acq_err_num >
>>>+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
>>>+			dev_err(ice_pf_to_dev(pf),
>>>+				"EEC/PPS DPLLs periodic work disabled\n");
>>>+			return;
>>>+		}
>>>+	}
>>>+	mutex_unlock(&d->lock);
>>>+	ice_dpll_notify_changes(de);
>>>+	ice_dpll_notify_changes(dp);
>>>+
>>>+	/* Run twice a second or reschedule if update failed */
>>>+	kthread_queue_delayed_work(d->kworker, &d->work,
>>>+				   ret ? msecs_to_jiffies(10) :
>>>+				   msecs_to_jiffies(500));
>>>+}

[...]


>>>+/**
>>>+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
>>>+ * @pf: board private structure
>>>+ *
>>>+ * Return:
>>>+ * * 0 - success
>>>+ * * negative - init failure
>>>+ */
>>>+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf)
>>>+{
>>>+	u64 clock_id = 0;
>>>+
>>>+	ice_generate_clock_id(pf, &clock_id);
>>>+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,
>>
>>I have to say I'm a bit lost in this code. Why exactly do you need this
>>here? Looks like the pointer was set in ice_dpll_init_dpll().
>>
>>Or, is that in case of a different PF instantiating the DPLL instances?
>
>Yes it is, different PF is attaching recovered clock pins with this.
>
>>If yes, I'm pretty sure what it is wrong. What is the PF which did
>>instanticate those unbinds? You have to share the dpll instance,
>>refcount it.
>>
>
>It will break, as in our case only one designated PF controls the dpll.

You need to fix this then.


>
>>Btw, you have a problem during init as well, as the order matters. What
>>if the other function probes only after executing this? You got -EFAULT
>>here and bail out.
>>
>
>We don't expect such use case, altough I see your point, will try to fix it.

What? You have to be kidding me, correct? User obviously should have
free will to use sysfs to bind/unbind the PCI devices in any order he
pleases.


>
>>In mlx5, I also share one dpll instance between 2 PFs. What I do is I
>>create mlx5-dpll instance which is refcounted, created by first probed
>>PF and removed by the last one. In mlx5 case, the PFs are equal, nobody
>>is an owner of the dpll. In your case, I think it is different. So
>>probably better to implement the logic in driver then in the dpll core.
>>
>
>For this NIC only one PF will control the dpll, so there is a designated owner.
>Here owner must not only initialize a dpll but also register its pin,
>so the other PFs could register additional pins. Basically it means
>for ice that we can only rely on some postponed rclk initialization for
>a case of unordered PF initialization. Seems doable.

My point is, you should have one DPLL instance shared for muptiple PFs.
Then, you have pin struct and dpll struct to use in pin_register and you
can avoid this odd description magic which is based obviously on broken
model you have.


>
>>Then you don't need dpll_device_get_by_clock_id at all. If you decide to
>>implement that in dpll core, I believe that there should be some
>>functions like:
>>dpll = dpll_device_get(ops, clock_id, ...)  - to create/get reference
>>dpll_device_put(dpll)                       - to put reference/destroy
>
>Sure, we can rename "dpll_device_get_by_clock_id" to "dpll_device_get" (as
>it is only function currently exported for such behavior), and add
>"dpll_device_put", with ref counts as suggested.
>
>>
>>First caller of dpll_device_get() actually makes dpll to instantiate the
>>device.
>>
>
>Maybe I am missing something.. do you suggest that "dpll_device_get" would
>allocate dpll device and do ref count, and then dpll_device_register(..) call

No need for separate register, is it? just have one dpll_device_get()
function allocate-register/getref for you. Why do you need anything else?


>would assign all the arguments that are available only in the owner instance?
>Or i got it wrong?

[...]


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

* Re: [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
@ 2023-01-31 13:00         ` Jiri Pirko
  0 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-31 13:00 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

Fri, Jan 27, 2023 at 07:13:20PM CET, arkadiusz.kubalewski@intel.com wrote:
>
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, January 19, 2023 3:54 PM
>>
>>Tue, Jan 17, 2023 at 07:00:51PM CET, vadfed@meta.com wrote:
>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>

[...]


>>>+/**
>>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>>+ * @work: pointer to kthread_work structure
>>>+ *
>>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>>+ */
>>>+static void ice_dpll_periodic_work(struct kthread_work *work)
>>>+{
>>>+	struct ice_dplls *d = container_of(work, struct ice_dplls,
>>>work.work);
>>>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>>>+	struct ice_dpll *de = &pf->dplls.eec;
>>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>>+	int ret = 0;
>>>+
>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>
>>Why do you need to check the flag there, this would should not be
>>ever scheduled in case the flag was not set.
>>
>
>It's here rather for stopping the worker, It shall no longer reschedule and
>bail out.

How that can happen?



>
>>
>>>+		return;
>>>+	mutex_lock(&d->lock);
>>>+	ret = ice_dpll_update_state(&pf->hw, de);
>>>+	if (!ret)
>>>+		ret = ice_dpll_update_state(&pf->hw, dp);
>>>+	if (ret) {
>>>+		d->cgu_state_acq_err_num++;
>>>+		/* stop rescheduling this worker */
>>>+		if (d->cgu_state_acq_err_num >
>>>+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
>>>+			dev_err(ice_pf_to_dev(pf),
>>>+				"EEC/PPS DPLLs periodic work disabled\n");
>>>+			return;
>>>+		}
>>>+	}
>>>+	mutex_unlock(&d->lock);
>>>+	ice_dpll_notify_changes(de);
>>>+	ice_dpll_notify_changes(dp);
>>>+
>>>+	/* Run twice a second or reschedule if update failed */
>>>+	kthread_queue_delayed_work(d->kworker, &d->work,
>>>+				   ret ? msecs_to_jiffies(10) :
>>>+				   msecs_to_jiffies(500));
>>>+}

[...]


>>>+/**
>>>+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
>>>+ * @pf: board private structure
>>>+ *
>>>+ * Return:
>>>+ * * 0 - success
>>>+ * * negative - init failure
>>>+ */
>>>+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf)
>>>+{
>>>+	u64 clock_id = 0;
>>>+
>>>+	ice_generate_clock_id(pf, &clock_id);
>>>+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,
>>
>>I have to say I'm a bit lost in this code. Why exactly do you need this
>>here? Looks like the pointer was set in ice_dpll_init_dpll().
>>
>>Or, is that in case of a different PF instantiating the DPLL instances?
>
>Yes it is, different PF is attaching recovered clock pins with this.
>
>>If yes, I'm pretty sure what it is wrong. What is the PF which did
>>instanticate those unbinds? You have to share the dpll instance,
>>refcount it.
>>
>
>It will break, as in our case only one designated PF controls the dpll.

You need to fix this then.


>
>>Btw, you have a problem during init as well, as the order matters. What
>>if the other function probes only after executing this? You got -EFAULT
>>here and bail out.
>>
>
>We don't expect such use case, altough I see your point, will try to fix it.

What? You have to be kidding me, correct? User obviously should have
free will to use sysfs to bind/unbind the PCI devices in any order he
pleases.


>
>>In mlx5, I also share one dpll instance between 2 PFs. What I do is I
>>create mlx5-dpll instance which is refcounted, created by first probed
>>PF and removed by the last one. In mlx5 case, the PFs are equal, nobody
>>is an owner of the dpll. In your case, I think it is different. So
>>probably better to implement the logic in driver then in the dpll core.
>>
>
>For this NIC only one PF will control the dpll, so there is a designated owner.
>Here owner must not only initialize a dpll but also register its pin,
>so the other PFs could register additional pins. Basically it means
>for ice that we can only rely on some postponed rclk initialization for
>a case of unordered PF initialization. Seems doable.

My point is, you should have one DPLL instance shared for muptiple PFs.
Then, you have pin struct and dpll struct to use in pin_register and you
can avoid this odd description magic which is based obviously on broken
model you have.


>
>>Then you don't need dpll_device_get_by_clock_id at all. If you decide to
>>implement that in dpll core, I believe that there should be some
>>functions like:
>>dpll = dpll_device_get(ops, clock_id, ...)  - to create/get reference
>>dpll_device_put(dpll)                       - to put reference/destroy
>
>Sure, we can rename "dpll_device_get_by_clock_id" to "dpll_device_get" (as
>it is only function currently exported for such behavior), and add
>"dpll_device_put", with ref counts as suggested.
>
>>
>>First caller of dpll_device_get() actually makes dpll to instantiate the
>>device.
>>
>
>Maybe I am missing something.. do you suggest that "dpll_device_get" would
>allocate dpll device and do ref count, and then dpll_device_register(..) call

No need for separate register, is it? just have one dpll_device_get()
function allocate-register/getref for you. Why do you need anything else?


>would assign all the arguments that are available only in the owner instance?
>Or i got it wrong?

[...]


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-01-27 18:12       ` Kubalewski, Arkadiusz
@ 2023-01-31 14:00         ` Jiri Pirko
  -1 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-31 14:00 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, January 19, 2023 6:16 PM
>>
>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:

[...]


>>>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>>
>>Hmm, don't you want to put an owner module as an arg here as well? I
>>don't see how could 2 modules sanely work with the same dpll instance.
>>
>
>Sorry, I don't get it.
>How the driver that needs to find a dpll would know the owner module?

Something like:
dpll = dpll_device_get(ops, THIS_MODULE, ...)
if (IS_ERR(dpll))
	..


>The idea of this is to let another driver instance to find a dpll device
>already registered in OS.
>The driver that is searching dpll device is not the same as the one that has
>created the device, otherwise it wouldn't make any sense?

You have to distinguish driver/driver_instance. It it is not the same
driver(module), something is seriously wrong here.


>
>>
>>>+						enum dpll_type type, u8 idx)
>>>+{
>>>+	struct dpll_device *dpll, *ret = NULL;
>>>+	unsigned long index;
>>>+
>>>+	mutex_lock(&dpll_device_xa_lock);
>>>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>>>+		if (dpll->clock_id == clock_id) {
>>>+			if (dpll->type == type) {
>>>+				if (dpll->dev_driver_idx == idx) {
>>>+					ret = dpll;
>>>+					break;
>>>+				}
>>>+			}
>>>+		}
>>>+	}
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+
>>>+	return ret;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
>>>+
>>>+static void dpll_device_release(struct device *dev)
>>>+{
>>>+	struct dpll_device *dpll;
>>>+
>>>+	mutex_lock(&dpll_device_xa_lock);
>>>+	dpll = to_dpll_device(dev);
>>>+	dpll_device_unregister(dpll);
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+	dpll_device_free(dpll);
>>>+}
>>>+
>>>+static struct class dpll_class = {
>>>+	.name = "dpll",
>>>+	.dev_release = dpll_device_release,
>>
>>Why do you want to do this? Why the driver cannot do
>>dpll_device_unregister/free() manually. I think it makes things easier
>>to read then to rely on dev garbage collector.
>>
>
>This was in the first version submitted by Vadim.
>I think we can remove it, unless someone has different view?

Cool.


>
>>
>>>+};
>>>+
>>>+struct dpll_device
>>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>>>+		   u8 dev_driver_idx, void *priv, struct device *parent)
>>>+{
>>>+	struct dpll_device *dpll;
>>>+	int ret;
>>>+
>>>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>>>+	if (!dpll)
>>>+		return ERR_PTR(-ENOMEM);
>>>+
>>>+	mutex_init(&dpll->lock);
>>>+	dpll->ops = ops;
>>>+	dpll->dev.class = &dpll_class;
>>>+	dpll->parent = parent;
>>>+	dpll->type = type;
>>>+	dpll->dev_driver_idx = dev_driver_idx;
>>>+	dpll->clock_id = clock_id;
>>>+	dpll->clock_class = clock_class;
>>>+
>>>+	mutex_lock(&dpll_device_xa_lock);
>>>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>>>+		       xa_limit_16b, GFP_KERNEL);
>>>+	if (ret)
>>>+		goto error;
>>>+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
>>>+		     dev_driver_idx);
>>>+	dpll->priv = priv;
>>>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>
>>What is exactly the point of using this mark?
>>
>
>I think this can be also removed now, as there is no separated alloc/register
>for newly created dpll device.

Cool.


>
>>
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+	dpll_notify_device_create(dpll);
>>>+
>>>+	return dpll;
>>>+
>>>+error:
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+	kfree(dpll);
>>>+	return ERR_PTR(ret);
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_device_alloc);

[...]


>>>+			return -EEXIST;
>>>+	}
>>>+
>>>+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
>>>+	if (!ret)
>>>+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);
>>
>>What is exactly the point of having this mark?
>>
>
>Think this could be now removed, we got rid of separated alloc/register for
>dpll device.

Cool.


>
>>
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device
>>>*dpll,
>>>+			    struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	struct dpll_pin_ref *ref, *pos;
>>>+	unsigned long index;
>>>+	u32 idx;
>>>+
>>>+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
>>>+	if (!ref)
>>>+		return -ENOMEM;
>>>+	ref->dpll = dpll;
>>>+	ref->ops = ops;
>>>+	ref->priv = priv;
>>>+	if (!xa_empty(&pin->ref_dplls)) {
>>
>>Pointless check. Just do iterate.
>>
>
>Sure, will do.
>
>>
>>>+		xa_for_each(&pin->ref_dplls, index, pos) {
>>>+			if (pos->dpll == ref->dpll)
>>>+				return -EEXIST;
>>>+		}
>>>+	}
>>>+
>>>+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b,
>>>GFP_KERNEL);
>>>+}
>>>+
>>>+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device
>>>*dpll)
>>>+{
>>>+	struct dpll_pin_ref *pos;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each(&pin->ref_dplls, index, pos) {
>>>+		if (pos->dpll == dpll) {
>>>+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
>>>+			break;
>>>+		}
>>>+	}
>>>+}
>>>+
>>>+static int pin_deregister_from_xa(struct xarray *xa_pins, struct dpll_pin
>>>*pin)
>>
>>1) dpll_ prefix
>
>Sure, will do.
>
>>2) "deregister" is odd name
>
>Rodger that, will fix.
>
>>3) why don't you have it next to dpll_alloc_pin_on_xa() as it is a
>>   symmetric function?
>
>Will do.
>
>>4) Why exactly just xa_erase() would not do?
>
>Might do, but need to rethink this :)

Great :)


>
>>
>>>+{
>>>+	struct dpll_pin *pos;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each(xa_pins, index, pos) {
>>>+		if (pos == pin) {
>>>+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));
>>
>>You have an odd pattern of functions getting pin as an arg then
>>doing lookup for the same pin. I have to be missing to see some
>>black magic here :O
>>
>
>The black magic was done to target correct pin in case pin index differs
>between dplls it was registered with. It would depend on the way shared pins
>are going to be allocated.
>If mixed pins approach is allowed (shared + non-shared pins) on any dpll, we
>would end up in situation where pin index for the same physical pin on multiple
>devices may be different, depending on registering pins order.
>
>As desribed in below comments, I can see here one simple solution: allow kernel
>module (which registers a pin with dpll) to control/assign pin index.
>The kernel module would only need take care of them being unique, when
>registers with first dpll - which seems not a problem. This way we would also
>be albe to get rid of searching pin function (as indexes would be known for all
>driver instances), different driver instances would use that index to share a
>pin.
>Also all the blackmagic like you described wouldn't be needed, thus simplifing
>a dpll subsystem.

Good.


>
>>
>>>+			return 0;
>>>+		}
>>>+	}
>>>+
>>>+	return -ENXIO;
>>>+}
>>>+
>>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>>+		      struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	int ret;
>>>+
>>>+	if (!pin || !ops)
>>>+		return -EINVAL;
>>>+
>>>+	mutex_lock(&dpll->lock);
>>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>>+	if (!ret) {
>>>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>>>+		if (ret)
>>>+			pin_deregister_from_xa(&dpll->pins, pin);
>>>+	}
>>>+	mutex_unlock(&dpll->lock);
>>>+	if (!ret)
>>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>>>+
>>>+	return ret;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>>+
>>>+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins, int
>>>idx)
>>>+{
>>>+	struct dpll_pin *pos;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
>>>+		if (pos->idx == idx)
>>>+			return pos;
>>>+	}
>>>+
>>>+	return NULL;
>>>+}
>>>+
>>>+/**
>>>+ * dpll_pin_get_by_idx - find a pin by its index
>>>+ * @dpll: dpll device pointer
>>>+ * @idx: index of pin
>>>+ *
>>>+ * Allows multiple driver instances using one physical DPLL to find
>>>+ * and share pin already registered with existing dpll device.
>>>+ *
>>>+ * Return: pointer if pin was found, NULL otherwise.
>>>+ */
>>>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
>>>+{
>>>+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
>>>+}
>>>+
>>>+	struct dpll_pin
>>>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char
>>>*description)
>>>+{
>>>+	struct dpll_pin *pos, *pin = NULL;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each(&dpll->pins, index, pos) {
>>>+		if (!strncmp(pos->description, description,
>>>+			     DPLL_PIN_DESC_LEN)) {
>>>+			pin = pos;
>>>+			break;
>>>+		}
>>>+	}
>>>+
>>>+	return pin;
>>>+}
>>>+
>>>+int
>>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>>+			 struct dpll_device *dpll,
>>>+			 const char *shared_pin_description,
>>
>>I don't follow why you need to pass the string. You have struct dpll_pin
>>* in the driver. Pass that instead, avoid string to refer to kernel
>>object. But this is something I wrote multiple times.
>>
>
>I wrote this so many times :) Separated driver instances doesn't have the pin
>object pointer by default (unless they share it through some unwanted static/
>global contatiners). They need to somehow target a pin, right now only unique
>attributes on dpll/pin pair are a description and index.
>Desription is a constant, index depends on the order of initialization and is
>internal for a dpll device.
>Previously there were a function to obtain a pin index by its description, then
>register with obtained index, now this is merged into one function.
>
>Altough I agree this is still not best aproach.
>I will fix by: fallback to targeting a pin to be shared by its index, with one
>slight design change, the pin index would have to be given by the driver
>instance which registers it with the first dpll.
>All the other separated driver instances which are using that pin will have to
>know the index assigned to the pin that is going to be shared, which seems
>like a best approach to fix this issue.

>
>>
>>>+			 struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	struct dpll_pin *pin;
>>>+	int ret;
>>>+
>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>+					  shared_pin_description);
>>>+	if (!pin) {
>>>+		ret = -EINVAL;
>>>+		goto unlock;
>>>+	}
>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>+unlock:
>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>+
>>>+	return ret;
>>
>>I don't understand why there should be a separate function to register
>>the shared pin. As I see it, there is a pin object that could be
>>registered with 2 or more dpll devices. What about having:
>>
>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>dpll_pin_register(dpll_1, pin);
>>dpll_pin_register(dpll_2, pin);
>>dpll_pin_register(dpll_3, pin);
>>
>
>IMHO your example works already, but it would possible only if the same driver
>instance initializes all dplls.

It should be only one instance of dpll to be shared between driver
instances as I wrote in the reply to the "ice" part. There might he some
pins created alongside with this.

My point is, the first driver instance which creates dpll registers also
the pins. The other driver instance does not do anything, just gets
reference to the dpll.

On cleanup path, the last driver instance tearing down would unregister
dpll pins (Could be done automatically by dpll_device_put()).

There might be some other pins (Synce) created per driver instance
(per-PF). You have to distinguish these 2 groups.


>dpll_shared_pin_register is designed for driver instances without the pin

I think we need to make sure the terms are correct "sharing" is between
multiple dpll instances. However, if 2 driver instances are sharing the
same dpll instance, this instance has pins. There is no sharing unless
there is another dpll instance in picture. Correct?


>object.
> 
>>Then one pin will we in 3 xa_arrays for 3 dplls.
>>
>
>As we can see dpll_shared_pin_register is a fancy wrapper for
>dpll_pin_register. So yeah, that's the point :) Just separated driver instances
>sharing a pin are a issue, will fix with the approach described above (pin
>index given by the registering driver instance).

Yeah, driver instances and dpll instances are not the same thing. I dpll
instance per physical dpll. Driver instances should share them.


>
>>
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);

[...]


>>>+/**
>>>+ * dpll_pin_parent - provide pin's parent pin if available
>>>+ * @pin: registered pin pointer
>>>+ *
>>>+ * Return: pointer to aparent if found, NULL otherwise.
>>>+ */
>>>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)
>>
>>What exactly is the reason of having one line helpers to access struct
>>fields for a struct which is known to the caller? Unneccesary
>>boilerplate code. Please remove these. For pin and for dpll_device as
>>well.
>>
>
>Actually dpll_pin is defined in dpll_core.c, so it is not known to the caller
>yet. About dpll_device, yes it is known. And we need common approach here, thus
>we need a fix. I know this is kernel code, full of hacks and performance related
>bad-design stuff, so will fix as suggested.

You are in the same code, just multiple files. Share the structs in .h
files internally. Externally (to the drivers), the struct geometry
should be hidden so the driver does not do some unwanted magic.


>
>>
>>
>>>+{
>>>+	return pin->parent_pin;
>>>+}
>>>+

[...]


>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>+				   const struct dpll_device *dpll,
>>>+				   const struct dpll_pin *pin)
>>>+{
>>>+	enum dpll_pin_mode i;
>>>+	bool active;
>>>+
>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>+			return 0;
>>>+		if (active)
>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>
>>Why this is signed?
>>
>
>Because enums are signed.

You use negative values in enums? Don't do that here. Have all netlink
atrributes unsigned please.


>
>>
>>>+				return -EMSGSIZE;
>>>+	}
>>>+
>>>+	return 0;
>>>+}
>>>+

[...]


>>>+static struct genl_family dpll_family __ro_after_init = {
>>>+	.hdrsize	= 0,
>>
>>No need for static.
>>
>
>Sorry, don't get it, why it shall be non-static?

Static is already zeroed, you don't need to zero it again.


>
>>
>>>+	.name		= DPLL_FAMILY_NAME,
>>>+	.version	= DPLL_VERSION,
>>>+	.ops		= dpll_ops,
>>>+	.n_ops		= ARRAY_SIZE(dpll_ops),
>>>+	.mcgrps		= dpll_mcgrps,
>>>+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
>>>+	.pre_doit	= dpll_pre_doit,
>>>+	.parallel_ops   = true,
>>>+};

[...]


>>>+
>>>+#define DPLL_FILTER_PINS	1
>>>+#define DPLL_FILTER_STATUS	2
>>
>>Why again do we need any filtering here?
>>
>
>A way to reduce output generated by dump/get requests. Assume the userspace
>want to have specific information instead of everything in one packet.
>They might be not needed after we introduce separated "get pin" command.

That's right, not needed.


>
>>
>>>+
>>>+/* dplla - Attributes of dpll generic netlink family
>>>+ *
>>>+ * @DPLLA_UNSPEC - invalid attribute
>>>+ * @DPLLA_ID - ID of a dpll device (unsigned int)
>>>+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH
>>size)
>>>+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
>>>+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum
>>dpll_mode)
>>>+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll
>>
>>IDX
>>
>
>Sure, will fix.
>
>>
>>>+ *	(unsigned int)
>>>+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
>>>+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)
>>
>>Hmm, wouldn't it be better to have it as 1/10 of Celsius degree for
>>example?
>>
>
>As we are not using it, I don't have any strong opinon on this, but seems
>resonable to me, will add a divider into uAPI like:
>
>#define DPLL_TEMP_DIVIDER	10

Okay.


>
>>
>>>+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
>>>+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum
>>dpll_clock_class)
>>>+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests
>>>(int,
>>>+ *	sum of DPLL_DUMP_FILTER_* defines)
>>>+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
>>>+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
>>>+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by
>>>driver
>>>+ *	(char array of PIN_DESC_LEN size)
>>>+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
>>>+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
>>>+ *	(enum dpll_pin_signal_type)
>>>+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
>>>+ *	(enum dpll_pin_signal_type)
>>>+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for
>>>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
>>>+ *	(unsigned int)
>>>+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
>>>+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
>>>+ *	(enum dpll_pin_mode)
>>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>>+ *	(enum dpll_change_type)
>>>+ **/
>>>+enum dplla {
>>>+	DPLLA_UNSPEC,
>>>+	DPLLA_ID,
>>>+	DPLLA_NAME,
>>>+	DPLLA_MODE,
>>>+	DPLLA_MODE_SUPPORTED,
>>>+	DPLLA_SOURCE_PIN_IDX,
>>>+	DPLLA_LOCK_STATUS,
>>>+	DPLLA_TEMP,
>>>+	DPLLA_CLOCK_ID,
>>>+	DPLLA_CLOCK_CLASS,
>>>+	DPLLA_FILTER,
>>>+	DPLLA_PIN,
>>>+	DPLLA_PIN_IDX,
>>>+	DPLLA_PIN_DESCRIPTION,
>>>+	DPLLA_PIN_TYPE,
>>>+	DPLLA_PIN_SIGNAL_TYPE,
>>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>>+	DPLLA_PIN_CUSTOM_FREQ,
>>>+	DPLLA_PIN_MODE,
>>>+	DPLLA_PIN_MODE_SUPPORTED,
>>>+	DPLLA_PIN_PRIO,
>>>+	DPLLA_PIN_PARENT_IDX,
>>>+	DPLLA_PIN_NETIFINDEX,
>>
>>I believe we cannot have this right now. The problem is, ifindexes may
>>overlay between namespaces. So ifindex without namespace means nothing.
>>I don't see how this can work from the dpll side.
>>
>
>I am a bit confused, as it seemed we already had an agreement on v4 discussion
>on this. And now again you highligheted it with the same reasoning as
>previously. Has anything changed on that matter?

Not sure what we discussed, but ifindex alone is not enough as ifindexes
from multiple namespaces overlap.


>
>>Lets assign dpll_pin pointer to netdev and expose it over RT netlink in
>>a similar way devlink_port is exposed. That should be enough for the
>>user to find a dpll instance for given netdev.
>>
>>It does not have to be part of this set strictly, but I would like to
>>have it here, so the full picture could be seen.
>>
>
>A "full picture" is getting broken if we would go with another place to keep
>the reference between pin and device that produces its signal.
>
>Why don't we add an 'struct device *' argument for dpll_pin_alloc, create
>new attribute with dev_name macro similary to the current name of dpll device
>name, something like: DPLLA_PIN_RCLK_DEVICE (string).
>This way any device (not only netdev) would be able to add a pin and point to
>a device which produces its signal.

Okay, that sounds good.


>
>>
>>
>>>+	DPLLA_CHANGE_TYPE,
>>>+	__DPLLA_MAX,
>>>+};
>>>+
>>>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>>>+
>>>+/* dpll_lock_status - DPLL status provides information of device status
>>>+ *
>>>+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
>>>+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid (or
>>is in
>>>+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
>>>+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid
>>signal
>>>+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
>>>+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a valid
>>lock
>>>+ *	or was forced by DPLL_MODE_HOLDOVER mode)
>>>+ **/
>>>+enum dpll_lock_status {
>>>+	DPLL_LOCK_STATUS_UNSPEC,
>>>+	DPLL_LOCK_STATUS_UNLOCKED,
>>>+	DPLL_LOCK_STATUS_CALIBRATING,
>>>+	DPLL_LOCK_STATUS_LOCKED,
>>>+	DPLL_LOCK_STATUS_HOLDOVER,
>>>+
>>>+	__DPLL_LOCK_STATUS_MAX,
>>>+};
>>>+
>>>+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
>>>+
>>>+/* dpll_pin_type - signal types
>>>+ *
>>>+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
>>>+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
>>>+ * @DPLL_PIN_TYPE_EXT - external source
>>>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>>>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>>>+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
>>>+ **/
>>>+enum dpll_pin_type {
>>>+	DPLL_PIN_TYPE_UNSPEC,
>>>+	DPLL_PIN_TYPE_MUX,
>>>+	DPLL_PIN_TYPE_EXT,
>>>+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>>+	DPLL_PIN_TYPE_INT_OSCILLATOR,
>>>+	DPLL_PIN_TYPE_GNSS,
>>>+
>>>+	__DPLL_PIN_TYPE_MAX,
>>>+};
>>>+
>>>+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
>>>+
>>>+/* dpll_pin_signal_type - signal types
>>>+ *
>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>
>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
>>with HZ value directly? For example, supported freq:
>>1, 10000000
>>or:
>>1, 1000
>>
>>freq set 10000000
>>freq set 1
>>
>>Simple and easy.
>>
>
>AFAIR, we wanted to have most commonly used frequencies as enums + custom_freq
>for some exotic ones (please note that there is also possible 2PPS, which is
>0.5 Hz).

In this exotic case, user might add divider netlink attribute to divide
the frequency pass in the attr. No problem.


>This was design decision we already agreed on.
>The userspace shall get definite list of comonly used frequencies that can be
>used with given HW, it clearly enums are good for this.

I don't see why. Each instance supports a set of frequencies. It would
pass the values to the userspace.

I fail to see the need to have some fixed values listed in enums. Mixing
approaches for a single attribute is wrong. In ethtool we also don't
have enum values for 10,100,1000mbits etc. It's just a number.


>
>>
>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value
>>defined
>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>+ **/
>>>+enum dpll_pin_signal_type {
>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>+
>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>+};
>>>+
>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>+
>>>+/* dpll_pin_mode - available pin states
>>>+ *
>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>>>+ **/
>>>+enum dpll_pin_mode {
>>>+	DPLL_PIN_MODE_UNSPEC,
>>>+	DPLL_PIN_MODE_CONNECTED,
>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>+	DPLL_PIN_MODE_SOURCE,
>>>+	DPLL_PIN_MODE_OUTPUT,
>>
>>I don't follow. I see 2 enums:
>>CONNECTED/DISCONNECTED
>>SOURCE/OUTPUT
>>why this is mangled together? How is it supposed to be working. Like a
>>bitarray?
>>
>
>The userspace shouldn't worry about bits, it recieves a list of attributes.
>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>DPLLA_PIN_MODE_SUPPORTED. I.e.
>
>	DPLLA_PIN_IDX			0
>	DPLLA_PIN_MODE			1,3
>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4

I believe that mixing apples and oranges in a single attr is not correct.
Could you please split to separate attrs as drafted below?

>
>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>and bitmask is not a way to go for userspace.

What? See nla_bitmap.

Anyway, why can't you have:
DPLLA_PIN_CONNECTED     u8 1/0 (bool)
DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }
DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED, CAN_CHANGE_DIRECTION)

We can use the capabilitis bitfield eventually for other purposes as
well, it is going to be handy I'm sure.



>
>
>>
>>>+
>>>+	__DPLL_PIN_MODE_MAX,
>>>+};
>>>+

[...]


>>>+/**
>>>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate how
>>>+ * dpll selects one of its sources to syntonize with a source.
>>>+ *
>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request
>>to dpll
>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>>dpll
>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>>
>>Why does the user care which oscilator is run internally. It's freerun,
>>isn't it? If you want to expose oscilator type, you should do it
>>elsewhere.
>>
>
>In NCO user might change frequency of an output, in freerun cannot.

How this could be done?


[...]


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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-01-31 14:00         ` Jiri Pirko
  0 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-01-31 14:00 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, January 19, 2023 6:16 PM
>>
>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:

[...]


>>>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>>
>>Hmm, don't you want to put an owner module as an arg here as well? I
>>don't see how could 2 modules sanely work with the same dpll instance.
>>
>
>Sorry, I don't get it.
>How the driver that needs to find a dpll would know the owner module?

Something like:
dpll = dpll_device_get(ops, THIS_MODULE, ...)
if (IS_ERR(dpll))
	..


>The idea of this is to let another driver instance to find a dpll device
>already registered in OS.
>The driver that is searching dpll device is not the same as the one that has
>created the device, otherwise it wouldn't make any sense?

You have to distinguish driver/driver_instance. It it is not the same
driver(module), something is seriously wrong here.


>
>>
>>>+						enum dpll_type type, u8 idx)
>>>+{
>>>+	struct dpll_device *dpll, *ret = NULL;
>>>+	unsigned long index;
>>>+
>>>+	mutex_lock(&dpll_device_xa_lock);
>>>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>>>+		if (dpll->clock_id == clock_id) {
>>>+			if (dpll->type == type) {
>>>+				if (dpll->dev_driver_idx == idx) {
>>>+					ret = dpll;
>>>+					break;
>>>+				}
>>>+			}
>>>+		}
>>>+	}
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+
>>>+	return ret;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
>>>+
>>>+static void dpll_device_release(struct device *dev)
>>>+{
>>>+	struct dpll_device *dpll;
>>>+
>>>+	mutex_lock(&dpll_device_xa_lock);
>>>+	dpll = to_dpll_device(dev);
>>>+	dpll_device_unregister(dpll);
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+	dpll_device_free(dpll);
>>>+}
>>>+
>>>+static struct class dpll_class = {
>>>+	.name = "dpll",
>>>+	.dev_release = dpll_device_release,
>>
>>Why do you want to do this? Why the driver cannot do
>>dpll_device_unregister/free() manually. I think it makes things easier
>>to read then to rely on dev garbage collector.
>>
>
>This was in the first version submitted by Vadim.
>I think we can remove it, unless someone has different view?

Cool.


>
>>
>>>+};
>>>+
>>>+struct dpll_device
>>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>>>+		   u8 dev_driver_idx, void *priv, struct device *parent)
>>>+{
>>>+	struct dpll_device *dpll;
>>>+	int ret;
>>>+
>>>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>>>+	if (!dpll)
>>>+		return ERR_PTR(-ENOMEM);
>>>+
>>>+	mutex_init(&dpll->lock);
>>>+	dpll->ops = ops;
>>>+	dpll->dev.class = &dpll_class;
>>>+	dpll->parent = parent;
>>>+	dpll->type = type;
>>>+	dpll->dev_driver_idx = dev_driver_idx;
>>>+	dpll->clock_id = clock_id;
>>>+	dpll->clock_class = clock_class;
>>>+
>>>+	mutex_lock(&dpll_device_xa_lock);
>>>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>>>+		       xa_limit_16b, GFP_KERNEL);
>>>+	if (ret)
>>>+		goto error;
>>>+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
>>>+		     dev_driver_idx);
>>>+	dpll->priv = priv;
>>>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>
>>What is exactly the point of using this mark?
>>
>
>I think this can be also removed now, as there is no separated alloc/register
>for newly created dpll device.

Cool.


>
>>
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+	dpll_notify_device_create(dpll);
>>>+
>>>+	return dpll;
>>>+
>>>+error:
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+	kfree(dpll);
>>>+	return ERR_PTR(ret);
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_device_alloc);

[...]


>>>+			return -EEXIST;
>>>+	}
>>>+
>>>+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
>>>+	if (!ret)
>>>+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);
>>
>>What is exactly the point of having this mark?
>>
>
>Think this could be now removed, we got rid of separated alloc/register for
>dpll device.

Cool.


>
>>
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device
>>>*dpll,
>>>+			    struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	struct dpll_pin_ref *ref, *pos;
>>>+	unsigned long index;
>>>+	u32 idx;
>>>+
>>>+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
>>>+	if (!ref)
>>>+		return -ENOMEM;
>>>+	ref->dpll = dpll;
>>>+	ref->ops = ops;
>>>+	ref->priv = priv;
>>>+	if (!xa_empty(&pin->ref_dplls)) {
>>
>>Pointless check. Just do iterate.
>>
>
>Sure, will do.
>
>>
>>>+		xa_for_each(&pin->ref_dplls, index, pos) {
>>>+			if (pos->dpll == ref->dpll)
>>>+				return -EEXIST;
>>>+		}
>>>+	}
>>>+
>>>+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b,
>>>GFP_KERNEL);
>>>+}
>>>+
>>>+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device
>>>*dpll)
>>>+{
>>>+	struct dpll_pin_ref *pos;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each(&pin->ref_dplls, index, pos) {
>>>+		if (pos->dpll == dpll) {
>>>+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
>>>+			break;
>>>+		}
>>>+	}
>>>+}
>>>+
>>>+static int pin_deregister_from_xa(struct xarray *xa_pins, struct dpll_pin
>>>*pin)
>>
>>1) dpll_ prefix
>
>Sure, will do.
>
>>2) "deregister" is odd name
>
>Rodger that, will fix.
>
>>3) why don't you have it next to dpll_alloc_pin_on_xa() as it is a
>>   symmetric function?
>
>Will do.
>
>>4) Why exactly just xa_erase() would not do?
>
>Might do, but need to rethink this :)

Great :)


>
>>
>>>+{
>>>+	struct dpll_pin *pos;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each(xa_pins, index, pos) {
>>>+		if (pos == pin) {
>>>+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));
>>
>>You have an odd pattern of functions getting pin as an arg then
>>doing lookup for the same pin. I have to be missing to see some
>>black magic here :O
>>
>
>The black magic was done to target correct pin in case pin index differs
>between dplls it was registered with. It would depend on the way shared pins
>are going to be allocated.
>If mixed pins approach is allowed (shared + non-shared pins) on any dpll, we
>would end up in situation where pin index for the same physical pin on multiple
>devices may be different, depending on registering pins order.
>
>As desribed in below comments, I can see here one simple solution: allow kernel
>module (which registers a pin with dpll) to control/assign pin index.
>The kernel module would only need take care of them being unique, when
>registers with first dpll - which seems not a problem. This way we would also
>be albe to get rid of searching pin function (as indexes would be known for all
>driver instances), different driver instances would use that index to share a
>pin.
>Also all the blackmagic like you described wouldn't be needed, thus simplifing
>a dpll subsystem.

Good.


>
>>
>>>+			return 0;
>>>+		}
>>>+	}
>>>+
>>>+	return -ENXIO;
>>>+}
>>>+
>>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>>+		      struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	int ret;
>>>+
>>>+	if (!pin || !ops)
>>>+		return -EINVAL;
>>>+
>>>+	mutex_lock(&dpll->lock);
>>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>>+	if (!ret) {
>>>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>>>+		if (ret)
>>>+			pin_deregister_from_xa(&dpll->pins, pin);
>>>+	}
>>>+	mutex_unlock(&dpll->lock);
>>>+	if (!ret)
>>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>>>+
>>>+	return ret;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>>+
>>>+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins, int
>>>idx)
>>>+{
>>>+	struct dpll_pin *pos;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
>>>+		if (pos->idx == idx)
>>>+			return pos;
>>>+	}
>>>+
>>>+	return NULL;
>>>+}
>>>+
>>>+/**
>>>+ * dpll_pin_get_by_idx - find a pin by its index
>>>+ * @dpll: dpll device pointer
>>>+ * @idx: index of pin
>>>+ *
>>>+ * Allows multiple driver instances using one physical DPLL to find
>>>+ * and share pin already registered with existing dpll device.
>>>+ *
>>>+ * Return: pointer if pin was found, NULL otherwise.
>>>+ */
>>>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
>>>+{
>>>+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
>>>+}
>>>+
>>>+	struct dpll_pin
>>>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char
>>>*description)
>>>+{
>>>+	struct dpll_pin *pos, *pin = NULL;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each(&dpll->pins, index, pos) {
>>>+		if (!strncmp(pos->description, description,
>>>+			     DPLL_PIN_DESC_LEN)) {
>>>+			pin = pos;
>>>+			break;
>>>+		}
>>>+	}
>>>+
>>>+	return pin;
>>>+}
>>>+
>>>+int
>>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>>+			 struct dpll_device *dpll,
>>>+			 const char *shared_pin_description,
>>
>>I don't follow why you need to pass the string. You have struct dpll_pin
>>* in the driver. Pass that instead, avoid string to refer to kernel
>>object. But this is something I wrote multiple times.
>>
>
>I wrote this so many times :) Separated driver instances doesn't have the pin
>object pointer by default (unless they share it through some unwanted static/
>global contatiners). They need to somehow target a pin, right now only unique
>attributes on dpll/pin pair are a description and index.
>Desription is a constant, index depends on the order of initialization and is
>internal for a dpll device.
>Previously there were a function to obtain a pin index by its description, then
>register with obtained index, now this is merged into one function.
>
>Altough I agree this is still not best aproach.
>I will fix by: fallback to targeting a pin to be shared by its index, with one
>slight design change, the pin index would have to be given by the driver
>instance which registers it with the first dpll.
>All the other separated driver instances which are using that pin will have to
>know the index assigned to the pin that is going to be shared, which seems
>like a best approach to fix this issue.

>
>>
>>>+			 struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	struct dpll_pin *pin;
>>>+	int ret;
>>>+
>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>+					  shared_pin_description);
>>>+	if (!pin) {
>>>+		ret = -EINVAL;
>>>+		goto unlock;
>>>+	}
>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>+unlock:
>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>+
>>>+	return ret;
>>
>>I don't understand why there should be a separate function to register
>>the shared pin. As I see it, there is a pin object that could be
>>registered with 2 or more dpll devices. What about having:
>>
>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>dpll_pin_register(dpll_1, pin);
>>dpll_pin_register(dpll_2, pin);
>>dpll_pin_register(dpll_3, pin);
>>
>
>IMHO your example works already, but it would possible only if the same driver
>instance initializes all dplls.

It should be only one instance of dpll to be shared between driver
instances as I wrote in the reply to the "ice" part. There might he some
pins created alongside with this.

My point is, the first driver instance which creates dpll registers also
the pins. The other driver instance does not do anything, just gets
reference to the dpll.

On cleanup path, the last driver instance tearing down would unregister
dpll pins (Could be done automatically by dpll_device_put()).

There might be some other pins (Synce) created per driver instance
(per-PF). You have to distinguish these 2 groups.


>dpll_shared_pin_register is designed for driver instances without the pin

I think we need to make sure the terms are correct "sharing" is between
multiple dpll instances. However, if 2 driver instances are sharing the
same dpll instance, this instance has pins. There is no sharing unless
there is another dpll instance in picture. Correct?


>object.
> 
>>Then one pin will we in 3 xa_arrays for 3 dplls.
>>
>
>As we can see dpll_shared_pin_register is a fancy wrapper for
>dpll_pin_register. So yeah, that's the point :) Just separated driver instances
>sharing a pin are a issue, will fix with the approach described above (pin
>index given by the registering driver instance).

Yeah, driver instances and dpll instances are not the same thing. I dpll
instance per physical dpll. Driver instances should share them.


>
>>
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);

[...]


>>>+/**
>>>+ * dpll_pin_parent - provide pin's parent pin if available
>>>+ * @pin: registered pin pointer
>>>+ *
>>>+ * Return: pointer to aparent if found, NULL otherwise.
>>>+ */
>>>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)
>>
>>What exactly is the reason of having one line helpers to access struct
>>fields for a struct which is known to the caller? Unneccesary
>>boilerplate code. Please remove these. For pin and for dpll_device as
>>well.
>>
>
>Actually dpll_pin is defined in dpll_core.c, so it is not known to the caller
>yet. About dpll_device, yes it is known. And we need common approach here, thus
>we need a fix. I know this is kernel code, full of hacks and performance related
>bad-design stuff, so will fix as suggested.

You are in the same code, just multiple files. Share the structs in .h
files internally. Externally (to the drivers), the struct geometry
should be hidden so the driver does not do some unwanted magic.


>
>>
>>
>>>+{
>>>+	return pin->parent_pin;
>>>+}
>>>+

[...]


>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>+				   const struct dpll_device *dpll,
>>>+				   const struct dpll_pin *pin)
>>>+{
>>>+	enum dpll_pin_mode i;
>>>+	bool active;
>>>+
>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>+			return 0;
>>>+		if (active)
>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>
>>Why this is signed?
>>
>
>Because enums are signed.

You use negative values in enums? Don't do that here. Have all netlink
atrributes unsigned please.


>
>>
>>>+				return -EMSGSIZE;
>>>+	}
>>>+
>>>+	return 0;
>>>+}
>>>+

[...]


>>>+static struct genl_family dpll_family __ro_after_init = {
>>>+	.hdrsize	= 0,
>>
>>No need for static.
>>
>
>Sorry, don't get it, why it shall be non-static?

Static is already zeroed, you don't need to zero it again.


>
>>
>>>+	.name		= DPLL_FAMILY_NAME,
>>>+	.version	= DPLL_VERSION,
>>>+	.ops		= dpll_ops,
>>>+	.n_ops		= ARRAY_SIZE(dpll_ops),
>>>+	.mcgrps		= dpll_mcgrps,
>>>+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
>>>+	.pre_doit	= dpll_pre_doit,
>>>+	.parallel_ops   = true,
>>>+};

[...]


>>>+
>>>+#define DPLL_FILTER_PINS	1
>>>+#define DPLL_FILTER_STATUS	2
>>
>>Why again do we need any filtering here?
>>
>
>A way to reduce output generated by dump/get requests. Assume the userspace
>want to have specific information instead of everything in one packet.
>They might be not needed after we introduce separated "get pin" command.

That's right, not needed.


>
>>
>>>+
>>>+/* dplla - Attributes of dpll generic netlink family
>>>+ *
>>>+ * @DPLLA_UNSPEC - invalid attribute
>>>+ * @DPLLA_ID - ID of a dpll device (unsigned int)
>>>+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH
>>size)
>>>+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
>>>+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum
>>dpll_mode)
>>>+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll
>>
>>IDX
>>
>
>Sure, will fix.
>
>>
>>>+ *	(unsigned int)
>>>+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
>>>+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)
>>
>>Hmm, wouldn't it be better to have it as 1/10 of Celsius degree for
>>example?
>>
>
>As we are not using it, I don't have any strong opinon on this, but seems
>resonable to me, will add a divider into uAPI like:
>
>#define DPLL_TEMP_DIVIDER	10

Okay.


>
>>
>>>+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
>>>+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum
>>dpll_clock_class)
>>>+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests
>>>(int,
>>>+ *	sum of DPLL_DUMP_FILTER_* defines)
>>>+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
>>>+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
>>>+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by
>>>driver
>>>+ *	(char array of PIN_DESC_LEN size)
>>>+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
>>>+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
>>>+ *	(enum dpll_pin_signal_type)
>>>+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
>>>+ *	(enum dpll_pin_signal_type)
>>>+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for
>>>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
>>>+ *	(unsigned int)
>>>+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
>>>+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
>>>+ *	(enum dpll_pin_mode)
>>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>>+ *	(enum dpll_change_type)
>>>+ **/
>>>+enum dplla {
>>>+	DPLLA_UNSPEC,
>>>+	DPLLA_ID,
>>>+	DPLLA_NAME,
>>>+	DPLLA_MODE,
>>>+	DPLLA_MODE_SUPPORTED,
>>>+	DPLLA_SOURCE_PIN_IDX,
>>>+	DPLLA_LOCK_STATUS,
>>>+	DPLLA_TEMP,
>>>+	DPLLA_CLOCK_ID,
>>>+	DPLLA_CLOCK_CLASS,
>>>+	DPLLA_FILTER,
>>>+	DPLLA_PIN,
>>>+	DPLLA_PIN_IDX,
>>>+	DPLLA_PIN_DESCRIPTION,
>>>+	DPLLA_PIN_TYPE,
>>>+	DPLLA_PIN_SIGNAL_TYPE,
>>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>>+	DPLLA_PIN_CUSTOM_FREQ,
>>>+	DPLLA_PIN_MODE,
>>>+	DPLLA_PIN_MODE_SUPPORTED,
>>>+	DPLLA_PIN_PRIO,
>>>+	DPLLA_PIN_PARENT_IDX,
>>>+	DPLLA_PIN_NETIFINDEX,
>>
>>I believe we cannot have this right now. The problem is, ifindexes may
>>overlay between namespaces. So ifindex without namespace means nothing.
>>I don't see how this can work from the dpll side.
>>
>
>I am a bit confused, as it seemed we already had an agreement on v4 discussion
>on this. And now again you highligheted it with the same reasoning as
>previously. Has anything changed on that matter?

Not sure what we discussed, but ifindex alone is not enough as ifindexes
from multiple namespaces overlap.


>
>>Lets assign dpll_pin pointer to netdev and expose it over RT netlink in
>>a similar way devlink_port is exposed. That should be enough for the
>>user to find a dpll instance for given netdev.
>>
>>It does not have to be part of this set strictly, but I would like to
>>have it here, so the full picture could be seen.
>>
>
>A "full picture" is getting broken if we would go with another place to keep
>the reference between pin and device that produces its signal.
>
>Why don't we add an 'struct device *' argument for dpll_pin_alloc, create
>new attribute with dev_name macro similary to the current name of dpll device
>name, something like: DPLLA_PIN_RCLK_DEVICE (string).
>This way any device (not only netdev) would be able to add a pin and point to
>a device which produces its signal.

Okay, that sounds good.


>
>>
>>
>>>+	DPLLA_CHANGE_TYPE,
>>>+	__DPLLA_MAX,
>>>+};
>>>+
>>>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>>>+
>>>+/* dpll_lock_status - DPLL status provides information of device status
>>>+ *
>>>+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
>>>+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid (or
>>is in
>>>+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
>>>+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid
>>signal
>>>+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
>>>+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a valid
>>lock
>>>+ *	or was forced by DPLL_MODE_HOLDOVER mode)
>>>+ **/
>>>+enum dpll_lock_status {
>>>+	DPLL_LOCK_STATUS_UNSPEC,
>>>+	DPLL_LOCK_STATUS_UNLOCKED,
>>>+	DPLL_LOCK_STATUS_CALIBRATING,
>>>+	DPLL_LOCK_STATUS_LOCKED,
>>>+	DPLL_LOCK_STATUS_HOLDOVER,
>>>+
>>>+	__DPLL_LOCK_STATUS_MAX,
>>>+};
>>>+
>>>+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
>>>+
>>>+/* dpll_pin_type - signal types
>>>+ *
>>>+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
>>>+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
>>>+ * @DPLL_PIN_TYPE_EXT - external source
>>>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>>>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>>>+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
>>>+ **/
>>>+enum dpll_pin_type {
>>>+	DPLL_PIN_TYPE_UNSPEC,
>>>+	DPLL_PIN_TYPE_MUX,
>>>+	DPLL_PIN_TYPE_EXT,
>>>+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>>+	DPLL_PIN_TYPE_INT_OSCILLATOR,
>>>+	DPLL_PIN_TYPE_GNSS,
>>>+
>>>+	__DPLL_PIN_TYPE_MAX,
>>>+};
>>>+
>>>+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
>>>+
>>>+/* dpll_pin_signal_type - signal types
>>>+ *
>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>
>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
>>with HZ value directly? For example, supported freq:
>>1, 10000000
>>or:
>>1, 1000
>>
>>freq set 10000000
>>freq set 1
>>
>>Simple and easy.
>>
>
>AFAIR, we wanted to have most commonly used frequencies as enums + custom_freq
>for some exotic ones (please note that there is also possible 2PPS, which is
>0.5 Hz).

In this exotic case, user might add divider netlink attribute to divide
the frequency pass in the attr. No problem.


>This was design decision we already agreed on.
>The userspace shall get definite list of comonly used frequencies that can be
>used with given HW, it clearly enums are good for this.

I don't see why. Each instance supports a set of frequencies. It would
pass the values to the userspace.

I fail to see the need to have some fixed values listed in enums. Mixing
approaches for a single attribute is wrong. In ethtool we also don't
have enum values for 10,100,1000mbits etc. It's just a number.


>
>>
>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value
>>defined
>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>+ **/
>>>+enum dpll_pin_signal_type {
>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>+
>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>+};
>>>+
>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>+
>>>+/* dpll_pin_mode - available pin states
>>>+ *
>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>>>+ **/
>>>+enum dpll_pin_mode {
>>>+	DPLL_PIN_MODE_UNSPEC,
>>>+	DPLL_PIN_MODE_CONNECTED,
>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>+	DPLL_PIN_MODE_SOURCE,
>>>+	DPLL_PIN_MODE_OUTPUT,
>>
>>I don't follow. I see 2 enums:
>>CONNECTED/DISCONNECTED
>>SOURCE/OUTPUT
>>why this is mangled together? How is it supposed to be working. Like a
>>bitarray?
>>
>
>The userspace shouldn't worry about bits, it recieves a list of attributes.
>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>DPLLA_PIN_MODE_SUPPORTED. I.e.
>
>	DPLLA_PIN_IDX			0
>	DPLLA_PIN_MODE			1,3
>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4

I believe that mixing apples and oranges in a single attr is not correct.
Could you please split to separate attrs as drafted below?

>
>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>and bitmask is not a way to go for userspace.

What? See nla_bitmap.

Anyway, why can't you have:
DPLLA_PIN_CONNECTED     u8 1/0 (bool)
DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }
DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED, CAN_CHANGE_DIRECTION)

We can use the capabilitis bitfield eventually for other purposes as
well, it is going to be handy I'm sure.



>
>
>>
>>>+
>>>+	__DPLL_PIN_MODE_MAX,
>>>+};
>>>+

[...]


>>>+/**
>>>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate how
>>>+ * dpll selects one of its sources to syntonize with a source.
>>>+ *
>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request
>>to dpll
>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>>dpll
>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>>
>>Why does the user care which oscilator is run internally. It's freerun,
>>isn't it? If you want to expose oscilator type, you should do it
>>elsewhere.
>>
>
>In NCO user might change frequency of an output, in freerun cannot.

How this could be done?


[...]


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* RE: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-01-31 14:00         ` Jiri Pirko
@ 2023-02-06  2:00           ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-02-06  2:00 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, January 31, 2023 3:01 PM
>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>Cc: Vadim Fedorenko <vadfed@meta.com>; Jakub Kicinski <kuba@kernel.org>;
>Jonathan Lemon <jonathan.lemon@gmail.com>; Paolo Abeni <pabeni@redhat.com>;
>netdev@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
>clk@vger.kernel.org; Olech, Milena <milena.olech@intel.com>; Michalik,
>Michal <michal.michalik@intel.com>
>Subject: Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
>
>Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, January 19, 2023 6:16 PM
>>>
>>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>>>>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>>>
>>>Hmm, don't you want to put an owner module as an arg here as well? I
>>>don't see how could 2 modules sanely work with the same dpll instance.
>>>
>>
>>Sorry, I don't get it.
>>How the driver that needs to find a dpll would know the owner module?
>
>Something like:
>dpll = dpll_device_get(ops, THIS_MODULE, ...)
>if (IS_ERR(dpll))
>	..
>

Oh yeah, sure, probably can do this.

>
>>The idea of this is to let another driver instance to find a dpll device
>>already registered in OS.
>>The driver that is searching dpll device is not the same as the one that
>>has
>>created the device, otherwise it wouldn't make any sense?
>
>You have to distinguish driver/driver_instance. It it is not the same
>driver(module), something is seriously wrong here.
>

Sure, will try to use THIS_MODULE as suggested in next version.

>
>>
>>>
>>>>+						enum dpll_type type, u8 idx)
>>>>+{
>>>>+	struct dpll_device *dpll, *ret = NULL;
>>>>+	unsigned long index;
>>>>+
>>>>+	mutex_lock(&dpll_device_xa_lock);
>>>>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>>>>+		if (dpll->clock_id == clock_id) {
>>>>+			if (dpll->type == type) {
>>>>+				if (dpll->dev_driver_idx == idx) {
>>>>+					ret = dpll;
>>>>+					break;
>>>>+				}
>>>>+			}
>>>>+		}
>>>>+	}
>>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>>+
>>>>+	return ret;
>>>>+}
>>>>+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
>>>>+
>>>>+static void dpll_device_release(struct device *dev)
>>>>+{
>>>>+	struct dpll_device *dpll;
>>>>+
>>>>+	mutex_lock(&dpll_device_xa_lock);
>>>>+	dpll = to_dpll_device(dev);
>>>>+	dpll_device_unregister(dpll);
>>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>>+	dpll_device_free(dpll);
>>>>+}
>>>>+
>>>>+static struct class dpll_class = {
>>>>+	.name = "dpll",
>>>>+	.dev_release = dpll_device_release,
>>>
>>>Why do you want to do this? Why the driver cannot do
>>>dpll_device_unregister/free() manually. I think it makes things easier
>>>to read then to rely on dev garbage collector.
>>>
>>
>>This was in the first version submitted by Vadim.
>>I think we can remove it, unless someone has different view?
>
>Cool.
>
>
>>
>>>
>>>>+};
>>>>+
>>>>+struct dpll_device
>>>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>>>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>>>>+		   u8 dev_driver_idx, void *priv, struct device *parent)
>>>>+{
>>>>+	struct dpll_device *dpll;
>>>>+	int ret;
>>>>+
>>>>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>>>>+	if (!dpll)
>>>>+		return ERR_PTR(-ENOMEM);
>>>>+
>>>>+	mutex_init(&dpll->lock);
>>>>+	dpll->ops = ops;
>>>>+	dpll->dev.class = &dpll_class;
>>>>+	dpll->parent = parent;
>>>>+	dpll->type = type;
>>>>+	dpll->dev_driver_idx = dev_driver_idx;
>>>>+	dpll->clock_id = clock_id;
>>>>+	dpll->clock_class = clock_class;
>>>>+
>>>>+	mutex_lock(&dpll_device_xa_lock);
>>>>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>>>>+		       xa_limit_16b, GFP_KERNEL);
>>>>+	if (ret)
>>>>+		goto error;
>>>>+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
>>>>+		     dev_driver_idx);
>>>>+	dpll->priv = priv;
>>>>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>>>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>>
>>>What is exactly the point of using this mark?
>>>
>>
>>I think this can be also removed now, as there is no separated
>>alloc/register
>>for newly created dpll device.
>
>Cool.
>
>
>>
>>>
>>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>>+	dpll_notify_device_create(dpll);
>>>>+
>>>>+	return dpll;
>>>>+
>>>>+error:
>>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>>+	kfree(dpll);
>>>>+	return ERR_PTR(ret);
>>>>+}
>>>>+EXPORT_SYMBOL_GPL(dpll_device_alloc);
>
>[...]
>
>
>>>>+			return -EEXIST;
>>>>+	}
>>>>+
>>>>+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
>>>>+	if (!ret)
>>>>+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);
>>>
>>>What is exactly the point of having this mark?
>>>
>>
>>Think this could be now removed, we got rid of separated alloc/register
>>for
>>dpll device.
>
>Cool.
>
>
>>
>>>
>>>>+
>>>>+	return ret;
>>>>+}
>>>>+
>>>>+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device
>>>>*dpll,
>>>>+			    struct dpll_pin_ops *ops, void *priv)
>>>>+{
>>>>+	struct dpll_pin_ref *ref, *pos;
>>>>+	unsigned long index;
>>>>+	u32 idx;
>>>>+
>>>>+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
>>>>+	if (!ref)
>>>>+		return -ENOMEM;
>>>>+	ref->dpll = dpll;
>>>>+	ref->ops = ops;
>>>>+	ref->priv = priv;
>>>>+	if (!xa_empty(&pin->ref_dplls)) {
>>>
>>>Pointless check. Just do iterate.
>>>
>>
>>Sure, will do.
>>
>>>
>>>>+		xa_for_each(&pin->ref_dplls, index, pos) {
>>>>+			if (pos->dpll == ref->dpll)
>>>>+				return -EEXIST;
>>>>+		}
>>>>+	}
>>>>+
>>>>+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b,
>>>>GFP_KERNEL);
>>>>+}
>>>>+
>>>>+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device
>>>>*dpll)
>>>>+{
>>>>+	struct dpll_pin_ref *pos;
>>>>+	unsigned long index;
>>>>+
>>>>+	xa_for_each(&pin->ref_dplls, index, pos) {
>>>>+		if (pos->dpll == dpll) {
>>>>+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
>>>>+			break;
>>>>+		}
>>>>+	}
>>>>+}
>>>>+
>>>>+static int pin_deregister_from_xa(struct xarray *xa_pins, struct
>>dpll_pin
>>>>*pin)
>>>
>>>1) dpll_ prefix
>>
>>Sure, will do.
>>
>>>2) "deregister" is odd name
>>
>>Rodger that, will fix.
>>
>>>3) why don't you have it next to dpll_alloc_pin_on_xa() as it is a
>>>   symmetric function?
>>
>>Will do.
>>
>>>4) Why exactly just xa_erase() would not do?
>>
>>Might do, but need to rethink this :)
>
>Great :)
>
>
>>
>>>
>>>>+{
>>>>+	struct dpll_pin *pos;
>>>>+	unsigned long index;
>>>>+
>>>>+	xa_for_each(xa_pins, index, pos) {
>>>>+		if (pos == pin) {
>>>>+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));
>>>
>>>You have an odd pattern of functions getting pin as an arg then
>>>doing lookup for the same pin. I have to be missing to see some
>>>black magic here :O
>>>
>>
>>The black magic was done to target correct pin in case pin index differs
>>between dplls it was registered with. It would depend on the way shared
>>pins
>>are going to be allocated.
>>If mixed pins approach is allowed (shared + non-shared pins) on any dpll,
>>we
>>would end up in situation where pin index for the same physical pin on
>>multiple
>>devices may be different, depending on registering pins order.
>>
>>As desribed in below comments, I can see here one simple solution: allow
>>kernel
>>module (which registers a pin with dpll) to control/assign pin index.
>>The kernel module would only need take care of them being unique, when
>>registers with first dpll - which seems not a problem. This way we would
>>also
>>be albe to get rid of searching pin function (as indexes would be known
>>for all
>>driver instances), different driver instances would use that index to
>>share a
>>pin.
>>Also all the blackmagic like you described wouldn't be needed, thus
>>simplifing
>>a dpll subsystem.
>
>Good.
>
>
>>
>>>
>>>>+			return 0;
>>>>+		}
>>>>+	}
>>>>+
>>>>+	return -ENXIO;
>>>>+}
>>>>+
>>>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>>>+		      struct dpll_pin_ops *ops, void *priv)
>>>>+{
>>>>+	int ret;
>>>>+
>>>>+	if (!pin || !ops)
>>>>+		return -EINVAL;
>>>>+
>>>>+	mutex_lock(&dpll->lock);
>>>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>>>+	if (!ret) {
>>>>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>>>>+		if (ret)
>>>>+			pin_deregister_from_xa(&dpll->pins, pin);
>>>>+	}
>>>>+	mutex_unlock(&dpll->lock);
>>>>+	if (!ret)
>>>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>>>>+
>>>>+	return ret;
>>>>+}
>>>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>>>+
>>>>+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins,
>>>>int
>>>>idx)
>>>>+{
>>>>+	struct dpll_pin *pos;
>>>>+	unsigned long index;
>>>>+
>>>>+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
>>>>+		if (pos->idx == idx)
>>>>+			return pos;
>>>>+	}
>>>>+
>>>>+	return NULL;
>>>>+}
>>>>+
>>>>+/**
>>>>+ * dpll_pin_get_by_idx - find a pin by its index
>>>>+ * @dpll: dpll device pointer
>>>>+ * @idx: index of pin
>>>>+ *
>>>>+ * Allows multiple driver instances using one physical DPLL to find
>>>>+ * and share pin already registered with existing dpll device.
>>>>+ *
>>>>+ * Return: pointer if pin was found, NULL otherwise.
>>>>+ */
>>>>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
>>>>+{
>>>>+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
>>>>+}
>>>>+
>>>>+	struct dpll_pin
>>>>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char
>>>>*description)
>>>>+{
>>>>+	struct dpll_pin *pos, *pin = NULL;
>>>>+	unsigned long index;
>>>>+
>>>>+	xa_for_each(&dpll->pins, index, pos) {
>>>>+		if (!strncmp(pos->description, description,
>>>>+			     DPLL_PIN_DESC_LEN)) {
>>>>+			pin = pos;
>>>>+			break;
>>>>+		}
>>>>+	}
>>>>+
>>>>+	return pin;
>>>>+}
>>>>+
>>>>+int
>>>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>>>+			 struct dpll_device *dpll,
>>>>+			 const char *shared_pin_description,
>>>
>>>I don't follow why you need to pass the string. You have struct dpll_pin
>>>* in the driver. Pass that instead, avoid string to refer to kernel
>>>object. But this is something I wrote multiple times.
>>>
>>
>>I wrote this so many times :) Separated driver instances doesn't have the
>>pin
>>object pointer by default (unless they share it through some unwanted
>>static/
>>global contatiners). They need to somehow target a pin, right now only
>>unique
>>attributes on dpll/pin pair are a description and index.
>>Desription is a constant, index depends on the order of initialization and
>>is
>>internal for a dpll device.
>>Previously there were a function to obtain a pin index by its description,
>>then
>>register with obtained index, now this is merged into one function.
>>
>>Altough I agree this is still not best aproach.
>>I will fix by: fallback to targeting a pin to be shared by its index, with
>>one
>>slight design change, the pin index would have to be given by the driver
>>instance which registers it with the first dpll.
>>All the other separated driver instances which are using that pin will
>>have to
>>know the index assigned to the pin that is going to be shared, which seems
>>like a best approach to fix this issue.
>
>>
>>>
>>>>+			 struct dpll_pin_ops *ops, void *priv)
>>>>+{
>>>>+	struct dpll_pin *pin;
>>>>+	int ret;
>>>>+
>>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>>+					  shared_pin_description);
>>>>+	if (!pin) {
>>>>+		ret = -EINVAL;
>>>>+		goto unlock;
>>>>+	}
>>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>>+unlock:
>>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>>+
>>>>+	return ret;
>>>
>>>I don't understand why there should be a separate function to register
>>>the shared pin. As I see it, there is a pin object that could be
>>>registered with 2 or more dpll devices. What about having:
>>>
>>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>>dpll_pin_register(dpll_1, pin);
>>>dpll_pin_register(dpll_2, pin);
>>>dpll_pin_register(dpll_3, pin);
>>>
>>
>>IMHO your example works already, but it would possible only if the same
>>driver
>>instance initializes all dplls.
>
>It should be only one instance of dpll to be shared between driver
>instances as I wrote in the reply to the "ice" part. There might he some
>pins created alongside with this.
>

pin = dpll_pin_alloc(desc, type, ops, priv)
dpll_pin_register(dpll_1, pin);
dpll_pin_register(dpll_2, pin);
dpll_pin_register(dpll_3, pin);
^ there is registration of a single pin by a 3 dpll instances, and a kernel
module instance which registers them has a reference to the pin and all dplls,
thus it can just register them all without any problems, don't need to call
dpll_shared_pin_register(..).

Now imagine 2 kernel module instances.
One (#1) creates one dpll, registers pins with it.
Second (#2) creates second dpll, and want to use/register pins of dpll
registered by the first instance (#1).

>My point is, the first driver instance which creates dpll registers also
>the pins. The other driver instance does not do anything, just gets
>reference to the dpll.
>
>On cleanup path, the last driver instance tearing down would unregister
>dpll pins (Could be done automatically by dpll_device_put()).
>
>There might be some other pins (Synce) created per driver instance
>(per-PF). You have to distinguish these 2 groups.
>
>
>>dpll_shared_pin_register is designed for driver instances without the pin
>
>I think we need to make sure the terms are correct "sharing" is between
>multiple dpll instances. However, if 2 driver instances are sharing the
>same dpll instance, this instance has pins. There is no sharing unless
>there is another dpll instance in picture. Correct?
>

Yes!
If two kernel module intances sharing a dpll instance, the pins belong
to the dpll instance, and yes each kernel module instance can register pins
with that dpll instance just with: dpll_pin_register(dpll_1, pin);

dpll_shared_pin_register(..) shall be used when separated kernel module
instances are initializing separated dpll instances, and those instances are
physically sharing their pins.

>
>>object.
>>
>>>Then one pin will we in 3 xa_arrays for 3 dplls.
>>>
>>
>>As we can see dpll_shared_pin_register is a fancy wrapper for
>>dpll_pin_register. So yeah, that's the point :) Just separated driver
>>instances
>>sharing a pin are a issue, will fix with the approach described above (pin
>>index given by the registering driver instance).
>
>Yeah, driver instances and dpll instances are not the same thing. I dpll
>instance per physical dpll. Driver instances should share them.
>

Sure thing, we aim that.

>
>>
>>>
>>>>+}
>>>>+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);
>
>[...]
>
>
>>>>+/**
>>>>+ * dpll_pin_parent - provide pin's parent pin if available
>>>>+ * @pin: registered pin pointer
>>>>+ *
>>>>+ * Return: pointer to aparent if found, NULL otherwise.
>>>>+ */
>>>>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)
>>>
>>>What exactly is the reason of having one line helpers to access struct
>>>fields for a struct which is known to the caller? Unneccesary
>>>boilerplate code. Please remove these. For pin and for dpll_device as
>>>well.
>>>
>>
>>Actually dpll_pin is defined in dpll_core.c, so it is not known to the
>>caller
>>yet. About dpll_device, yes it is known. And we need common approach here,
>>thus
>>we need a fix. I know this is kernel code, full of hacks and performance
>>related
>>bad-design stuff, so will fix as suggested.
>
>You are in the same code, just multiple files. Share the structs in .h
>files internally. Externally (to the drivers), the struct geometry
>should be hidden so the driver does not do some unwanted magic.
>

Yes, will do.

>
>>
>>>
>>>
>>>>+{
>>>>+	return pin->parent_pin;
>>>>+}
>>>>+
>
>[...]
>
>
>>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>>+				   const struct dpll_device *dpll,
>>>>+				   const struct dpll_pin *pin)
>>>>+{
>>>>+	enum dpll_pin_mode i;
>>>>+	bool active;
>>>>+
>>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>>+			return 0;
>>>>+		if (active)
>>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>>
>>>Why this is signed?
>>>
>>
>>Because enums are signed.
>
>You use negative values in enums? Don't do that here. Have all netlink
>atrributes unsigned please.
>

No, we don't use negative values, but enum is a signed type by itself.
Doesn't seem right thing to do, put signed-type value into unsigned type TLV.
This smells very bad.
 
>
>>
>>>
>>>>+				return -EMSGSIZE;
>>>>+	}
>>>>+
>>>>+	return 0;
>>>>+}
>>>>+
>
>[...]
>
>
>>>>+static struct genl_family dpll_family __ro_after_init = {
>>>>+	.hdrsize	= 0,
>>>
>>>No need for static.
>>>
>>
>>Sorry, don't get it, why it shall be non-static?
>
>Static is already zeroed, you don't need to zero it again.
>

Sure, got it.

>
>>
>>>
>>>>+	.name		= DPLL_FAMILY_NAME,
>>>>+	.version	= DPLL_VERSION,
>>>>+	.ops		= dpll_ops,
>>>>+	.n_ops		= ARRAY_SIZE(dpll_ops),
>>>>+	.mcgrps		= dpll_mcgrps,
>>>>+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
>>>>+	.pre_doit	= dpll_pre_doit,
>>>>+	.parallel_ops   = true,
>>>>+};
>
>[...]
>
>
>>>>+
>>>>+#define DPLL_FILTER_PINS	1
>>>>+#define DPLL_FILTER_STATUS	2
>>>
>>>Why again do we need any filtering here?
>>>
>>
>>A way to reduce output generated by dump/get requests. Assume the
>>userspace
>>want to have specific information instead of everything in one packet.
>>They might be not needed after we introduce separated "get pin" command.
>
>That's right, not needed.
>
>
>>
>>>
>>>>+
>>>>+/* dplla - Attributes of dpll generic netlink family
>>>>+ *
>>>>+ * @DPLLA_UNSPEC - invalid attribute
>>>>+ * @DPLLA_ID - ID of a dpll device (unsigned int)
>>>>+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH
>>>size)
>>>>+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
>>>>+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum
>>>dpll_mode)
>>>>+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll
>>>
>>>IDX
>>>
>>
>>Sure, will fix.
>>
>>>
>>>>+ *	(unsigned int)
>>>>+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
>>>>+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)
>>>
>>>Hmm, wouldn't it be better to have it as 1/10 of Celsius degree for
>>>example?
>>>
>>
>>As we are not using it, I don't have any strong opinon on this, but seems
>>resonable to me, will add a divider into uAPI like:
>>
>>#define DPLL_TEMP_DIVIDER	10
>
>Okay.
>
>
>>
>>>
>>>>+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
>>>>+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum
>>>dpll_clock_class)
>>>>+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests
>>>>(int,
>>>>+ *	sum of DPLL_DUMP_FILTER_* defines)
>>>>+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
>>>>+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
>>>>+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by
>>>>driver
>>>>+ *	(char array of PIN_DESC_LEN size)
>>>>+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
>>>>+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
>>>>+ *	(enum dpll_pin_signal_type)
>>>>+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
>>>>+ *	(enum dpll_pin_signal_type)
>>>>+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for
>>>>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
>>>>+ *	(unsigned int)
>>>>+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
>>>>+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
>>>>+ *	(enum dpll_pin_mode)
>>>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>>>+ *	(enum dpll_change_type)
>>>>+ **/
>>>>+enum dplla {
>>>>+	DPLLA_UNSPEC,
>>>>+	DPLLA_ID,
>>>>+	DPLLA_NAME,
>>>>+	DPLLA_MODE,
>>>>+	DPLLA_MODE_SUPPORTED,
>>>>+	DPLLA_SOURCE_PIN_IDX,
>>>>+	DPLLA_LOCK_STATUS,
>>>>+	DPLLA_TEMP,
>>>>+	DPLLA_CLOCK_ID,
>>>>+	DPLLA_CLOCK_CLASS,
>>>>+	DPLLA_FILTER,
>>>>+	DPLLA_PIN,
>>>>+	DPLLA_PIN_IDX,
>>>>+	DPLLA_PIN_DESCRIPTION,
>>>>+	DPLLA_PIN_TYPE,
>>>>+	DPLLA_PIN_SIGNAL_TYPE,
>>>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>>>+	DPLLA_PIN_CUSTOM_FREQ,
>>>>+	DPLLA_PIN_MODE,
>>>>+	DPLLA_PIN_MODE_SUPPORTED,
>>>>+	DPLLA_PIN_PRIO,
>>>>+	DPLLA_PIN_PARENT_IDX,
>>>>+	DPLLA_PIN_NETIFINDEX,
>>>
>>>I believe we cannot have this right now. The problem is, ifindexes may
>>>overlay between namespaces. So ifindex without namespace means nothing.
>>>I don't see how this can work from the dpll side.
>>>
>>
>>I am a bit confused, as it seemed we already had an agreement on v4
>discussion
>>on this. And now again you highligheted it with the same reasoning as
>>previously. Has anything changed on that matter?
>
>Not sure what we discussed, but ifindex alone is not enough as ifindexes
>from multiple namespaces overlap.
>
>
>>
>>>Lets assign dpll_pin pointer to netdev and expose it over RT netlink in
>>>a similar way devlink_port is exposed. That should be enough for the
>>>user to find a dpll instance for given netdev.
>>>
>>>It does not have to be part of this set strictly, but I would like to
>>>have it here, so the full picture could be seen.
>>>
>>
>>A "full picture" is getting broken if we would go with another place to
>>keep
>>the reference between pin and device that produces its signal.
>>
>>Why don't we add an 'struct device *' argument for dpll_pin_alloc, create
>>new attribute with dev_name macro similary to the current name of dpll
>>device
>>name, something like: DPLLA_PIN_RCLK_DEVICE (string).
>>This way any device (not only netdev) would be able to add a pin and point
>>to
>>a device which produces its signal.
>
>Okay, that sounds good.
>
>
>>
>>>
>>>
>>>>+	DPLLA_CHANGE_TYPE,
>>>>+	__DPLLA_MAX,
>>>>+};
>>>>+
>>>>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>>>>+
>>>>+/* dpll_lock_status - DPLL status provides information of device status
>>>>+ *
>>>>+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
>>>>+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid
>>(or
>>>is in
>>>>+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
>>>>+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid
>>>signal
>>>>+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
>>>>+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a
>>valid
>>>lock
>>>>+ *	or was forced by DPLL_MODE_HOLDOVER mode)
>>>>+ **/
>>>>+enum dpll_lock_status {
>>>>+	DPLL_LOCK_STATUS_UNSPEC,
>>>>+	DPLL_LOCK_STATUS_UNLOCKED,
>>>>+	DPLL_LOCK_STATUS_CALIBRATING,
>>>>+	DPLL_LOCK_STATUS_LOCKED,
>>>>+	DPLL_LOCK_STATUS_HOLDOVER,
>>>>+
>>>>+	__DPLL_LOCK_STATUS_MAX,
>>>>+};
>>>>+
>>>>+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
>>>>+
>>>>+/* dpll_pin_type - signal types
>>>>+ *
>>>>+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
>>>>+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
>>>>+ * @DPLL_PIN_TYPE_EXT - external source
>>>>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>>>>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>>>>+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
>>>>+ **/
>>>>+enum dpll_pin_type {
>>>>+	DPLL_PIN_TYPE_UNSPEC,
>>>>+	DPLL_PIN_TYPE_MUX,
>>>>+	DPLL_PIN_TYPE_EXT,
>>>>+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>>>+	DPLL_PIN_TYPE_INT_OSCILLATOR,
>>>>+	DPLL_PIN_TYPE_GNSS,
>>>>+
>>>>+	__DPLL_PIN_TYPE_MAX,
>>>>+};
>>>>+
>>>>+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
>>>>+
>>>>+/* dpll_pin_signal_type - signal types
>>>>+ *
>>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>>
>>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
>>>with HZ value directly? For example, supported freq:
>>>1, 10000000
>>>or:
>>>1, 1000
>>>
>>>freq set 10000000
>>>freq set 1
>>>
>>>Simple and easy.
>>>
>>
>>AFAIR, we wanted to have most commonly used frequencies as enums +
>>custom_freq
>>for some exotic ones (please note that there is also possible 2PPS, which
>>is
>>0.5 Hz).
>
>In this exotic case, user might add divider netlink attribute to divide
>the frequency pass in the attr. No problem.
>
>
>>This was design decision we already agreed on.
>>The userspace shall get definite list of comonly used frequencies that can
>>be
>>used with given HW, it clearly enums are good for this.
>
>I don't see why. Each instance supports a set of frequencies. It would
>pass the values to the userspace.
>
>I fail to see the need to have some fixed values listed in enums. Mixing
>approaches for a single attribute is wrong. In ethtool we also don't
>have enum values for 10,100,1000mbits etc. It's just a number.
>

In ethtool there are defines for linkspeeds.
There must be list of defines/enums to check the driver if it is supported.
Especially for ANY_FREQ we don't want to call driver 25 milions times or more.

Also, we have to move supported frequencies to the dpll_pin_alloc as it is
constant argument, supported frequencies shall not change @ runtime?
In such case there seems to be only one way to pass in a nice way, as a
bitmask?

Back to the userspace part, do you suggest to have DPLLA_PIN_FREQ attribute
and translate kernelspace enum values to userspace defines like 
DPLL_FREQ_1_HZ, etc? also with special define for supported ones ANY_FREQ?

>
>>
>>>
>>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value
>>>>defined
>>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>>+ **/
>>>>+enum dpll_pin_signal_type {
>>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>>+
>>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>>+};
>>>>+
>>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>>+
>>>>+/* dpll_pin_mode - available pin states
>>>>+ *
>>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>>>>+ **/
>>>>+enum dpll_pin_mode {
>>>>+	DPLL_PIN_MODE_UNSPEC,
>>>>+	DPLL_PIN_MODE_CONNECTED,
>>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>>+	DPLL_PIN_MODE_SOURCE,
>>>>+	DPLL_PIN_MODE_OUTPUT,
>>>
>>>I don't follow. I see 2 enums:
>>>CONNECTED/DISCONNECTED
>>>SOURCE/OUTPUT
>>>why this is mangled together? How is it supposed to be working. Like a
>>>bitarray?
>>>
>>
>>The userspace shouldn't worry about bits, it recieves a list of
>attributes.
>>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>>DPLLA_PIN_MODE_SUPPORTED. I.e.
>>
>>	DPLLA_PIN_IDX			0
>>	DPLLA_PIN_MODE			1,3
>>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4
>
>I believe that mixing apples and oranges in a single attr is not correct.
>Could you please split to separate attrs as drafted below?
>
>>
>>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>>and bitmask is not a way to go for userspace.
>
>What? See nla_bitmap.
>

AFAIK, nla_bitmap is not yet merged.

>Anyway, why can't you have:
>DPLLA_PIN_CONNECTED     u8 1/0 (bool)
>DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }

Don't get it, why this shall be u8 with bool value, doesn't make much sense for
userspace.
All the other attributes have enum type, we can go with separated attribute:
DPLLA_PIN_STATE		enum { CONNECTED/DISCONNECTED }
Just be consistent and clear, and yes u8 is enough it to keep it, as well as
all of attribute enum values, so we can use u8 instead of u32 for all of them.

Actually for "connected/disconnected"-part there are 2 valid use-cases on my
mind:
- pin can be connected with a number of "parents" (dplls or muxed-pins)
- pin is disconnected entirely
Second case can be achieved with control over first one, thus not need for any
special approach here. Proper control would be to let userspace connect or
disconnect a pin per each node it can be connected with, right?

Then example dump of "get-pins" could look like this:
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		0
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_EXT
	DPLLA_PIN_DIRECTION	SOURCE
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		CONNECTED
	DPLLA_DPLL			(nested)
		DPLLA_ID		1
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED

DPLL_PIN	(nested)
	DPLLA_PIN_IDX		1
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
	DPLLA_PIN_DIRECTION	SOURCE
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED
	DPLLA_DPLL			(nested)
		DPLLA_ID		1
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		CONNECTED

DPLL_PIN	(nested)	
	DPLLA_PIN_IDX		2
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
	DPLLA_PIN_DIRECTION	SOURCE
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED
	DPLLA_DPLL			(nested)
		DPLLA_ID		1
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED

(similar for muxed pins)
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		3
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
	DPLLA_PIN_DIRECTION	SOURCE
	DPLLA_PIN_PARENT		(nested)
		DPLLA_PIN_IDX		1
		DPLLA_PIN_STATE		DISCONNECTED
	DPLLA_PIN_PARENT		(nested)
		DPLLA_PIN_IDX		2
		DPLLA_PIN_STATE		CONNECTED
			
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		4
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
	DPLLA_PIN_DIRECTION	SOURCE
	DPLLA_PIN_PARENT		(nested)
		DPLLA_PIN_IDX		1
		DPLLA_PIN_STATE		CONNECTED
	DPLLA_PIN_PARENT		(nested)
		DPLLA_PIN_IDX		2
		DPLLA_PIN_STATE		DISCONNECTED

For DPLL_MODE_MANUAL a DPLLA_PIN_STATE would serve also as signal selector
mechanism.
In above example DPLL_ID=0 has only "connected" DPLL_PIN_IDX=0, now when
different pin "connect" is requested:

dpll-set request:
DPLLA_DPLL	(nested)
	DPLLA_ID=0
	DPLLA_NAME=pci_0000:00:00.0
DPLLA_PIN
	DPLLA_PIN_IDX=2
	DPLLA_PIN_CONNECTED=1

Former shall "disconnect"..
And now, dump pin-get:
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		0
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED
...
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		2
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		CONNECTED
		
At least that shall happen on hardware level, right?

As I can't find a use-case to have a pin "connected" but not "selected" in case
of DPLL_MODE_MANUAL.

A bit different is with DPLL_MODE_AUTOMATIC, the pins that connects with dpll
directly could be all connected, and their selection is auto-controlled with a
DPLLA_PIN_PRIO.
But still the user may also request to disconnect a pin - not use it at all
(instead of configuring lowest priority - which allows to use it, if all other
pins propagate invalid signal).

Thus, for DPLL_MODE_AUTOMATIC all ablove is the same with a one difference,
each pin/dpll pair would have a prio, like suggested in the other email.
DPLLA_PIN	(nested)
	...
	DPLLA_DPLL	(nested)
		...
		DPLLA_PIN_CONNECTED	<connected value>
		DPLLA_PIN_STATE		<prio value>

Which basically means that both DPLL_A_PIN_PRIO and DPLLA_PIN_STATE
shall be a property of a PIN-DPLL pair, and configured as such.


>DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED,
>CAN_CHANGE_DIRECTION)
>
>We can use the capabilitis bitfield eventually for other purposes as
>well, it is going to be handy I'm sure.
>

Well, in general I like the idea, altough the details...
We have 3 configuration levels:
- DPLL
- DPLL/PIN
- PIN

Considering that, there is space for 3 of such CAPABILITIES attributes, but:
- DPLL can only configure MODE for now, so we can only convert
DPLL_A_MODE_SUPPORTED to a bitfield, and add DPLL_CAPS later if required
- DPLL/PIN pair has configurable DPLLA_PIN_PRIO and DPLLA_PIN_STATE, so we
could introduce DPLLA_PIN_DPLL_CAPS for them
- PIN has now configurable frequency (but this is done by providing list of
supported ones - no need for extra attribute). We already know that pin shall
also have optional features, like phase offset, embedded sync.
For embedded sync if supported it shall also be a set of supported frequencies.
Possibly for phase offset we could use similar CAPS field, but don't think will
manage this into next version.

>
>
>>
>>
>>>
>>>>+
>>>>+	__DPLL_PIN_MODE_MAX,
>>>>+};
>>>>+
>
>[...]
>
>
>>>>+/**
>>>>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate
>>>>>how
>>>>+ * dpll selects one of its sources to syntonize with a source.
>>>>+ *
>>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request
>>>>to dpll
>>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>>>>dpll
>>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover
>>>>available
>>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>>>
>>>Why does the user care which oscilator is run internally. It's freerun,
>>>isn't it? If you want to expose oscilator type, you should do it
>>>elsewhere.
>>>
>>
>>In NCO user might change frequency of an output, in freerun cannot.
>
>How this could be done?
>

I guess by some internal synchronizer frequency dividers. Same as other output
(different then input) frequencies are achievable there.

Thanks,
Arkadiusz

>
>[...]


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

* RE: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-02-06  2:00           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-02-06  2:00 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, January 31, 2023 3:01 PM
>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>Cc: Vadim Fedorenko <vadfed@meta.com>; Jakub Kicinski <kuba@kernel.org>;
>Jonathan Lemon <jonathan.lemon@gmail.com>; Paolo Abeni <pabeni@redhat.com>;
>netdev@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
>clk@vger.kernel.org; Olech, Milena <milena.olech@intel.com>; Michalik,
>Michal <michal.michalik@intel.com>
>Subject: Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
>
>Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, January 19, 2023 6:16 PM
>>>
>>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>>>>+struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>>>
>>>Hmm, don't you want to put an owner module as an arg here as well? I
>>>don't see how could 2 modules sanely work with the same dpll instance.
>>>
>>
>>Sorry, I don't get it.
>>How the driver that needs to find a dpll would know the owner module?
>
>Something like:
>dpll = dpll_device_get(ops, THIS_MODULE, ...)
>if (IS_ERR(dpll))
>	..
>

Oh yeah, sure, probably can do this.

>
>>The idea of this is to let another driver instance to find a dpll device
>>already registered in OS.
>>The driver that is searching dpll device is not the same as the one that
>>has
>>created the device, otherwise it wouldn't make any sense?
>
>You have to distinguish driver/driver_instance. It it is not the same
>driver(module), something is seriously wrong here.
>

Sure, will try to use THIS_MODULE as suggested in next version.

>
>>
>>>
>>>>+						enum dpll_type type, u8 idx)
>>>>+{
>>>>+	struct dpll_device *dpll, *ret = NULL;
>>>>+	unsigned long index;
>>>>+
>>>>+	mutex_lock(&dpll_device_xa_lock);
>>>>+	xa_for_each_marked(&dpll_device_xa, index, dpll, DPLL_REGISTERED) {
>>>>+		if (dpll->clock_id == clock_id) {
>>>>+			if (dpll->type == type) {
>>>>+				if (dpll->dev_driver_idx == idx) {
>>>>+					ret = dpll;
>>>>+					break;
>>>>+				}
>>>>+			}
>>>>+		}
>>>>+	}
>>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>>+
>>>>+	return ret;
>>>>+}
>>>>+EXPORT_SYMBOL_GPL(dpll_device_get_by_clock_id);
>>>>+
>>>>+static void dpll_device_release(struct device *dev)
>>>>+{
>>>>+	struct dpll_device *dpll;
>>>>+
>>>>+	mutex_lock(&dpll_device_xa_lock);
>>>>+	dpll = to_dpll_device(dev);
>>>>+	dpll_device_unregister(dpll);
>>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>>+	dpll_device_free(dpll);
>>>>+}
>>>>+
>>>>+static struct class dpll_class = {
>>>>+	.name = "dpll",
>>>>+	.dev_release = dpll_device_release,
>>>
>>>Why do you want to do this? Why the driver cannot do
>>>dpll_device_unregister/free() manually. I think it makes things easier
>>>to read then to rely on dev garbage collector.
>>>
>>
>>This was in the first version submitted by Vadim.
>>I think we can remove it, unless someone has different view?
>
>Cool.
>
>
>>
>>>
>>>>+};
>>>>+
>>>>+struct dpll_device
>>>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>>>+		   const u64 clock_id, enum dpll_clock_class clock_class,
>>>>+		   u8 dev_driver_idx, void *priv, struct device *parent)
>>>>+{
>>>>+	struct dpll_device *dpll;
>>>>+	int ret;
>>>>+
>>>>+	dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>>>>+	if (!dpll)
>>>>+		return ERR_PTR(-ENOMEM);
>>>>+
>>>>+	mutex_init(&dpll->lock);
>>>>+	dpll->ops = ops;
>>>>+	dpll->dev.class = &dpll_class;
>>>>+	dpll->parent = parent;
>>>>+	dpll->type = type;
>>>>+	dpll->dev_driver_idx = dev_driver_idx;
>>>>+	dpll->clock_id = clock_id;
>>>>+	dpll->clock_class = clock_class;
>>>>+
>>>>+	mutex_lock(&dpll_device_xa_lock);
>>>>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>>>>+		       xa_limit_16b, GFP_KERNEL);
>>>>+	if (ret)
>>>>+		goto error;
>>>>+	dev_set_name(&dpll->dev, "dpll_%s_%d_%d", dev_name(parent), type,
>>>>+		     dev_driver_idx);
>>>>+	dpll->priv = priv;
>>>>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>>>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>>
>>>What is exactly the point of using this mark?
>>>
>>
>>I think this can be also removed now, as there is no separated
>>alloc/register
>>for newly created dpll device.
>
>Cool.
>
>
>>
>>>
>>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>>+	dpll_notify_device_create(dpll);
>>>>+
>>>>+	return dpll;
>>>>+
>>>>+error:
>>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>>+	kfree(dpll);
>>>>+	return ERR_PTR(ret);
>>>>+}
>>>>+EXPORT_SYMBOL_GPL(dpll_device_alloc);
>
>[...]
>
>
>>>>+			return -EEXIST;
>>>>+	}
>>>>+
>>>>+	ret = xa_alloc(pins, &pin->idx, pin, xa_limit_16b, GFP_KERNEL);
>>>>+	if (!ret)
>>>>+		xa_set_mark(pins, pin->idx, PIN_REGISTERED);
>>>
>>>What is exactly the point of having this mark?
>>>
>>
>>Think this could be now removed, we got rid of separated alloc/register
>>for
>>dpll device.
>
>Cool.
>
>
>>
>>>
>>>>+
>>>>+	return ret;
>>>>+}
>>>>+
>>>>+static int dpll_pin_ref_add(struct dpll_pin *pin, struct dpll_device
>>>>*dpll,
>>>>+			    struct dpll_pin_ops *ops, void *priv)
>>>>+{
>>>>+	struct dpll_pin_ref *ref, *pos;
>>>>+	unsigned long index;
>>>>+	u32 idx;
>>>>+
>>>>+	ref = kzalloc(sizeof(struct dpll_pin_ref), GFP_KERNEL);
>>>>+	if (!ref)
>>>>+		return -ENOMEM;
>>>>+	ref->dpll = dpll;
>>>>+	ref->ops = ops;
>>>>+	ref->priv = priv;
>>>>+	if (!xa_empty(&pin->ref_dplls)) {
>>>
>>>Pointless check. Just do iterate.
>>>
>>
>>Sure, will do.
>>
>>>
>>>>+		xa_for_each(&pin->ref_dplls, index, pos) {
>>>>+			if (pos->dpll == ref->dpll)
>>>>+				return -EEXIST;
>>>>+		}
>>>>+	}
>>>>+
>>>>+	return xa_alloc(&pin->ref_dplls, &idx, ref, xa_limit_16b,
>>>>GFP_KERNEL);
>>>>+}
>>>>+
>>>>+static void dpll_pin_ref_del(struct dpll_pin *pin, struct dpll_device
>>>>*dpll)
>>>>+{
>>>>+	struct dpll_pin_ref *pos;
>>>>+	unsigned long index;
>>>>+
>>>>+	xa_for_each(&pin->ref_dplls, index, pos) {
>>>>+		if (pos->dpll == dpll) {
>>>>+			WARN_ON_ONCE(pos != xa_erase(&pin->ref_dplls, index));
>>>>+			break;
>>>>+		}
>>>>+	}
>>>>+}
>>>>+
>>>>+static int pin_deregister_from_xa(struct xarray *xa_pins, struct
>>dpll_pin
>>>>*pin)
>>>
>>>1) dpll_ prefix
>>
>>Sure, will do.
>>
>>>2) "deregister" is odd name
>>
>>Rodger that, will fix.
>>
>>>3) why don't you have it next to dpll_alloc_pin_on_xa() as it is a
>>>   symmetric function?
>>
>>Will do.
>>
>>>4) Why exactly just xa_erase() would not do?
>>
>>Might do, but need to rethink this :)
>
>Great :)
>
>
>>
>>>
>>>>+{
>>>>+	struct dpll_pin *pos;
>>>>+	unsigned long index;
>>>>+
>>>>+	xa_for_each(xa_pins, index, pos) {
>>>>+		if (pos == pin) {
>>>>+			WARN_ON_ONCE(pos != xa_erase(xa_pins, index));
>>>
>>>You have an odd pattern of functions getting pin as an arg then
>>>doing lookup for the same pin. I have to be missing to see some
>>>black magic here :O
>>>
>>
>>The black magic was done to target correct pin in case pin index differs
>>between dplls it was registered with. It would depend on the way shared
>>pins
>>are going to be allocated.
>>If mixed pins approach is allowed (shared + non-shared pins) on any dpll,
>>we
>>would end up in situation where pin index for the same physical pin on
>>multiple
>>devices may be different, depending on registering pins order.
>>
>>As desribed in below comments, I can see here one simple solution: allow
>>kernel
>>module (which registers a pin with dpll) to control/assign pin index.
>>The kernel module would only need take care of them being unique, when
>>registers with first dpll - which seems not a problem. This way we would
>>also
>>be albe to get rid of searching pin function (as indexes would be known
>>for all
>>driver instances), different driver instances would use that index to
>>share a
>>pin.
>>Also all the blackmagic like you described wouldn't be needed, thus
>>simplifing
>>a dpll subsystem.
>
>Good.
>
>
>>
>>>
>>>>+			return 0;
>>>>+		}
>>>>+	}
>>>>+
>>>>+	return -ENXIO;
>>>>+}
>>>>+
>>>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>>>+		      struct dpll_pin_ops *ops, void *priv)
>>>>+{
>>>>+	int ret;
>>>>+
>>>>+	if (!pin || !ops)
>>>>+		return -EINVAL;
>>>>+
>>>>+	mutex_lock(&dpll->lock);
>>>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>>>+	if (!ret) {
>>>>+		ret = dpll_pin_ref_add(pin, dpll, ops, priv);
>>>>+		if (ret)
>>>>+			pin_deregister_from_xa(&dpll->pins, pin);
>>>>+	}
>>>>+	mutex_unlock(&dpll->lock);
>>>>+	if (!ret)
>>>>+		dpll_pin_notify(dpll, pin, DPLL_CHANGE_PIN_ADD);
>>>>+
>>>>+	return ret;
>>>>+}
>>>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>>>+
>>>>+struct dpll_pin *dpll_pin_get_by_idx_from_xa(struct xarray *xa_pins,
>>>>int
>>>>idx)
>>>>+{
>>>>+	struct dpll_pin *pos;
>>>>+	unsigned long index;
>>>>+
>>>>+	xa_for_each_marked(xa_pins, index, pos, PIN_REGISTERED) {
>>>>+		if (pos->idx == idx)
>>>>+			return pos;
>>>>+	}
>>>>+
>>>>+	return NULL;
>>>>+}
>>>>+
>>>>+/**
>>>>+ * dpll_pin_get_by_idx - find a pin by its index
>>>>+ * @dpll: dpll device pointer
>>>>+ * @idx: index of pin
>>>>+ *
>>>>+ * Allows multiple driver instances using one physical DPLL to find
>>>>+ * and share pin already registered with existing dpll device.
>>>>+ *
>>>>+ * Return: pointer if pin was found, NULL otherwise.
>>>>+ */
>>>>+struct dpll_pin *dpll_pin_get_by_idx(struct dpll_device *dpll, int idx)
>>>>+{
>>>>+	return dpll_pin_get_by_idx_from_xa(&dpll->pins, idx);
>>>>+}
>>>>+
>>>>+	struct dpll_pin
>>>>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char
>>>>*description)
>>>>+{
>>>>+	struct dpll_pin *pos, *pin = NULL;
>>>>+	unsigned long index;
>>>>+
>>>>+	xa_for_each(&dpll->pins, index, pos) {
>>>>+		if (!strncmp(pos->description, description,
>>>>+			     DPLL_PIN_DESC_LEN)) {
>>>>+			pin = pos;
>>>>+			break;
>>>>+		}
>>>>+	}
>>>>+
>>>>+	return pin;
>>>>+}
>>>>+
>>>>+int
>>>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>>>+			 struct dpll_device *dpll,
>>>>+			 const char *shared_pin_description,
>>>
>>>I don't follow why you need to pass the string. You have struct dpll_pin
>>>* in the driver. Pass that instead, avoid string to refer to kernel
>>>object. But this is something I wrote multiple times.
>>>
>>
>>I wrote this so many times :) Separated driver instances doesn't have the
>>pin
>>object pointer by default (unless they share it through some unwanted
>>static/
>>global contatiners). They need to somehow target a pin, right now only
>>unique
>>attributes on dpll/pin pair are a description and index.
>>Desription is a constant, index depends on the order of initialization and
>>is
>>internal for a dpll device.
>>Previously there were a function to obtain a pin index by its description,
>>then
>>register with obtained index, now this is merged into one function.
>>
>>Altough I agree this is still not best aproach.
>>I will fix by: fallback to targeting a pin to be shared by its index, with
>>one
>>slight design change, the pin index would have to be given by the driver
>>instance which registers it with the first dpll.
>>All the other separated driver instances which are using that pin will
>>have to
>>know the index assigned to the pin that is going to be shared, which seems
>>like a best approach to fix this issue.
>
>>
>>>
>>>>+			 struct dpll_pin_ops *ops, void *priv)
>>>>+{
>>>>+	struct dpll_pin *pin;
>>>>+	int ret;
>>>>+
>>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>>+					  shared_pin_description);
>>>>+	if (!pin) {
>>>>+		ret = -EINVAL;
>>>>+		goto unlock;
>>>>+	}
>>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>>+unlock:
>>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>>+
>>>>+	return ret;
>>>
>>>I don't understand why there should be a separate function to register
>>>the shared pin. As I see it, there is a pin object that could be
>>>registered with 2 or more dpll devices. What about having:
>>>
>>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>>dpll_pin_register(dpll_1, pin);
>>>dpll_pin_register(dpll_2, pin);
>>>dpll_pin_register(dpll_3, pin);
>>>
>>
>>IMHO your example works already, but it would possible only if the same
>>driver
>>instance initializes all dplls.
>
>It should be only one instance of dpll to be shared between driver
>instances as I wrote in the reply to the "ice" part. There might he some
>pins created alongside with this.
>

pin = dpll_pin_alloc(desc, type, ops, priv)
dpll_pin_register(dpll_1, pin);
dpll_pin_register(dpll_2, pin);
dpll_pin_register(dpll_3, pin);
^ there is registration of a single pin by a 3 dpll instances, and a kernel
module instance which registers them has a reference to the pin and all dplls,
thus it can just register them all without any problems, don't need to call
dpll_shared_pin_register(..).

Now imagine 2 kernel module instances.
One (#1) creates one dpll, registers pins with it.
Second (#2) creates second dpll, and want to use/register pins of dpll
registered by the first instance (#1).

>My point is, the first driver instance which creates dpll registers also
>the pins. The other driver instance does not do anything, just gets
>reference to the dpll.
>
>On cleanup path, the last driver instance tearing down would unregister
>dpll pins (Could be done automatically by dpll_device_put()).
>
>There might be some other pins (Synce) created per driver instance
>(per-PF). You have to distinguish these 2 groups.
>
>
>>dpll_shared_pin_register is designed for driver instances without the pin
>
>I think we need to make sure the terms are correct "sharing" is between
>multiple dpll instances. However, if 2 driver instances are sharing the
>same dpll instance, this instance has pins. There is no sharing unless
>there is another dpll instance in picture. Correct?
>

Yes!
If two kernel module intances sharing a dpll instance, the pins belong
to the dpll instance, and yes each kernel module instance can register pins
with that dpll instance just with: dpll_pin_register(dpll_1, pin);

dpll_shared_pin_register(..) shall be used when separated kernel module
instances are initializing separated dpll instances, and those instances are
physically sharing their pins.

>
>>object.
>>
>>>Then one pin will we in 3 xa_arrays for 3 dplls.
>>>
>>
>>As we can see dpll_shared_pin_register is a fancy wrapper for
>>dpll_pin_register. So yeah, that's the point :) Just separated driver
>>instances
>>sharing a pin are a issue, will fix with the approach described above (pin
>>index given by the registering driver instance).
>
>Yeah, driver instances and dpll instances are not the same thing. I dpll
>instance per physical dpll. Driver instances should share them.
>

Sure thing, we aim that.

>
>>
>>>
>>>>+}
>>>>+EXPORT_SYMBOL_GPL(dpll_shared_pin_register);
>
>[...]
>
>
>>>>+/**
>>>>+ * dpll_pin_parent - provide pin's parent pin if available
>>>>+ * @pin: registered pin pointer
>>>>+ *
>>>>+ * Return: pointer to aparent if found, NULL otherwise.
>>>>+ */
>>>>+struct dpll_pin *dpll_pin_parent(struct dpll_pin *pin)
>>>
>>>What exactly is the reason of having one line helpers to access struct
>>>fields for a struct which is known to the caller? Unneccesary
>>>boilerplate code. Please remove these. For pin and for dpll_device as
>>>well.
>>>
>>
>>Actually dpll_pin is defined in dpll_core.c, so it is not known to the
>>caller
>>yet. About dpll_device, yes it is known. And we need common approach here,
>>thus
>>we need a fix. I know this is kernel code, full of hacks and performance
>>related
>>bad-design stuff, so will fix as suggested.
>
>You are in the same code, just multiple files. Share the structs in .h
>files internally. Externally (to the drivers), the struct geometry
>should be hidden so the driver does not do some unwanted magic.
>

Yes, will do.

>
>>
>>>
>>>
>>>>+{
>>>>+	return pin->parent_pin;
>>>>+}
>>>>+
>
>[...]
>
>
>>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>>+				   const struct dpll_device *dpll,
>>>>+				   const struct dpll_pin *pin)
>>>>+{
>>>>+	enum dpll_pin_mode i;
>>>>+	bool active;
>>>>+
>>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>>+			return 0;
>>>>+		if (active)
>>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>>
>>>Why this is signed?
>>>
>>
>>Because enums are signed.
>
>You use negative values in enums? Don't do that here. Have all netlink
>atrributes unsigned please.
>

No, we don't use negative values, but enum is a signed type by itself.
Doesn't seem right thing to do, put signed-type value into unsigned type TLV.
This smells very bad.
 
>
>>
>>>
>>>>+				return -EMSGSIZE;
>>>>+	}
>>>>+
>>>>+	return 0;
>>>>+}
>>>>+
>
>[...]
>
>
>>>>+static struct genl_family dpll_family __ro_after_init = {
>>>>+	.hdrsize	= 0,
>>>
>>>No need for static.
>>>
>>
>>Sorry, don't get it, why it shall be non-static?
>
>Static is already zeroed, you don't need to zero it again.
>

Sure, got it.

>
>>
>>>
>>>>+	.name		= DPLL_FAMILY_NAME,
>>>>+	.version	= DPLL_VERSION,
>>>>+	.ops		= dpll_ops,
>>>>+	.n_ops		= ARRAY_SIZE(dpll_ops),
>>>>+	.mcgrps		= dpll_mcgrps,
>>>>+	.n_mcgrps	= ARRAY_SIZE(dpll_mcgrps),
>>>>+	.pre_doit	= dpll_pre_doit,
>>>>+	.parallel_ops   = true,
>>>>+};
>
>[...]
>
>
>>>>+
>>>>+#define DPLL_FILTER_PINS	1
>>>>+#define DPLL_FILTER_STATUS	2
>>>
>>>Why again do we need any filtering here?
>>>
>>
>>A way to reduce output generated by dump/get requests. Assume the
>>userspace
>>want to have specific information instead of everything in one packet.
>>They might be not needed after we introduce separated "get pin" command.
>
>That's right, not needed.
>
>
>>
>>>
>>>>+
>>>>+/* dplla - Attributes of dpll generic netlink family
>>>>+ *
>>>>+ * @DPLLA_UNSPEC - invalid attribute
>>>>+ * @DPLLA_ID - ID of a dpll device (unsigned int)
>>>>+ * @DPLLA_NAME - human-readable name (char array of DPLL_NAME_LENGTH
>>>size)
>>>>+ * @DPLLA_MODE - working mode of dpll (enum dpll_mode)
>>>>+ * @DPLLA_MODE_SUPPORTED - list of supported working modes (enum
>>>dpll_mode)
>>>>+ * @DPLLA_SOURCE_PIN_ID - ID of source pin selected to drive dpll
>>>
>>>IDX
>>>
>>
>>Sure, will fix.
>>
>>>
>>>>+ *	(unsigned int)
>>>>+ * @DPLLA_LOCK_STATUS - dpll's lock status (enum dpll_lock_status)
>>>>+ * @DPLLA_TEMP - dpll's temperature (signed int - Celsius degrees)
>>>
>>>Hmm, wouldn't it be better to have it as 1/10 of Celsius degree for
>>>example?
>>>
>>
>>As we are not using it, I don't have any strong opinon on this, but seems
>>resonable to me, will add a divider into uAPI like:
>>
>>#define DPLL_TEMP_DIVIDER	10
>
>Okay.
>
>
>>
>>>
>>>>+ * @DPLLA_CLOCK_ID - Unique Clock Identifier of dpll (u64)
>>>>+ * @DPLLA_CLOCK_CLASS - clock quality class of dpll (enum
>>>dpll_clock_class)
>>>>+ * @DPLLA_FILTER - filter bitmask for filtering get and dump requests
>>>>(int,
>>>>+ *	sum of DPLL_DUMP_FILTER_* defines)
>>>>+ * @DPLLA_PIN - nested attribute, each contains single pin attributes
>>>>+ * @DPLLA_PIN_IDX - index of a pin on dpll (unsigned int)
>>>>+ * @DPLLA_PIN_DESCRIPTION - human-readable pin description provided by
>>>>driver
>>>>+ *	(char array of PIN_DESC_LEN size)
>>>>+ * @DPLLA_PIN_TYPE - current type of a pin (enum dpll_pin_type)
>>>>+ * @DPLLA_PIN_SIGNAL_TYPE - current type of a signal
>>>>+ *	(enum dpll_pin_signal_type)
>>>>+ * @DPLLA_PIN_SIGNAL_TYPE_SUPPORTED - pin signal types supported
>>>>+ *	(enum dpll_pin_signal_type)
>>>>+ * @DPLLA_PIN_CUSTOM_FREQ - freq value for
>>>>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ
>>>>+ *	(unsigned int)
>>>>+ * @DPLLA_PIN_MODE - state of pin's capabilities (enum dpll_pin_mode)
>>>>+ * @DPLLA_PIN_MODE_SUPPORTED - available pin's capabilities
>>>>+ *	(enum dpll_pin_mode)
>>>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>>>+ *	(enum dpll_change_type)
>>>>+ **/
>>>>+enum dplla {
>>>>+	DPLLA_UNSPEC,
>>>>+	DPLLA_ID,
>>>>+	DPLLA_NAME,
>>>>+	DPLLA_MODE,
>>>>+	DPLLA_MODE_SUPPORTED,
>>>>+	DPLLA_SOURCE_PIN_IDX,
>>>>+	DPLLA_LOCK_STATUS,
>>>>+	DPLLA_TEMP,
>>>>+	DPLLA_CLOCK_ID,
>>>>+	DPLLA_CLOCK_CLASS,
>>>>+	DPLLA_FILTER,
>>>>+	DPLLA_PIN,
>>>>+	DPLLA_PIN_IDX,
>>>>+	DPLLA_PIN_DESCRIPTION,
>>>>+	DPLLA_PIN_TYPE,
>>>>+	DPLLA_PIN_SIGNAL_TYPE,
>>>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>>>+	DPLLA_PIN_CUSTOM_FREQ,
>>>>+	DPLLA_PIN_MODE,
>>>>+	DPLLA_PIN_MODE_SUPPORTED,
>>>>+	DPLLA_PIN_PRIO,
>>>>+	DPLLA_PIN_PARENT_IDX,
>>>>+	DPLLA_PIN_NETIFINDEX,
>>>
>>>I believe we cannot have this right now. The problem is, ifindexes may
>>>overlay between namespaces. So ifindex without namespace means nothing.
>>>I don't see how this can work from the dpll side.
>>>
>>
>>I am a bit confused, as it seemed we already had an agreement on v4
>discussion
>>on this. And now again you highligheted it with the same reasoning as
>>previously. Has anything changed on that matter?
>
>Not sure what we discussed, but ifindex alone is not enough as ifindexes
>from multiple namespaces overlap.
>
>
>>
>>>Lets assign dpll_pin pointer to netdev and expose it over RT netlink in
>>>a similar way devlink_port is exposed. That should be enough for the
>>>user to find a dpll instance for given netdev.
>>>
>>>It does not have to be part of this set strictly, but I would like to
>>>have it here, so the full picture could be seen.
>>>
>>
>>A "full picture" is getting broken if we would go with another place to
>>keep
>>the reference between pin and device that produces its signal.
>>
>>Why don't we add an 'struct device *' argument for dpll_pin_alloc, create
>>new attribute with dev_name macro similary to the current name of dpll
>>device
>>name, something like: DPLLA_PIN_RCLK_DEVICE (string).
>>This way any device (not only netdev) would be able to add a pin and point
>>to
>>a device which produces its signal.
>
>Okay, that sounds good.
>
>
>>
>>>
>>>
>>>>+	DPLLA_CHANGE_TYPE,
>>>>+	__DPLLA_MAX,
>>>>+};
>>>>+
>>>>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>>>>+
>>>>+/* dpll_lock_status - DPLL status provides information of device status
>>>>+ *
>>>>+ * @DPLL_LOCK_STATUS_UNSPEC - unspecified value
>>>>+ * @DPLL_LOCK_STATUS_UNLOCKED - dpll was not yet locked to any valid
>>(or
>>>is in
>>>>+ *	DPLL_MODE_FREERUN/DPLL_MODE_NCO modes)
>>>>+ * @DPLL_LOCK_STATUS_CALIBRATING - dpll is trying to lock to a valid
>>>signal
>>>>+ * @DPLL_LOCK_STATUS_LOCKED - dpll is locked
>>>>+ * @DPLL_LOCK_STATUS_HOLDOVER - dpll is in holdover state - lost a
>>valid
>>>lock
>>>>+ *	or was forced by DPLL_MODE_HOLDOVER mode)
>>>>+ **/
>>>>+enum dpll_lock_status {
>>>>+	DPLL_LOCK_STATUS_UNSPEC,
>>>>+	DPLL_LOCK_STATUS_UNLOCKED,
>>>>+	DPLL_LOCK_STATUS_CALIBRATING,
>>>>+	DPLL_LOCK_STATUS_LOCKED,
>>>>+	DPLL_LOCK_STATUS_HOLDOVER,
>>>>+
>>>>+	__DPLL_LOCK_STATUS_MAX,
>>>>+};
>>>>+
>>>>+#define DPLL_LOCK_STATUS_MAX (__DPLL_LOCK_STATUS_MAX - 1)
>>>>+
>>>>+/* dpll_pin_type - signal types
>>>>+ *
>>>>+ * @DPLL_PIN_TYPE_UNSPEC - unspecified value
>>>>+ * @DPLL_PIN_TYPE_MUX - mux type pin, aggregates selectable pins
>>>>+ * @DPLL_PIN_TYPE_EXT - external source
>>>>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>>>>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>>>>+ * @DPLL_PIN_TYPE_GNSS - GNSS recovered clock
>>>>+ **/
>>>>+enum dpll_pin_type {
>>>>+	DPLL_PIN_TYPE_UNSPEC,
>>>>+	DPLL_PIN_TYPE_MUX,
>>>>+	DPLL_PIN_TYPE_EXT,
>>>>+	DPLL_PIN_TYPE_SYNCE_ETH_PORT,
>>>>+	DPLL_PIN_TYPE_INT_OSCILLATOR,
>>>>+	DPLL_PIN_TYPE_GNSS,
>>>>+
>>>>+	__DPLL_PIN_TYPE_MAX,
>>>>+};
>>>>+
>>>>+#define DPLL_PIN_TYPE_MAX (__DPLL_PIN_TYPE_MAX - 1)
>>>>+
>>>>+/* dpll_pin_signal_type - signal types
>>>>+ *
>>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>>
>>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
>>>with HZ value directly? For example, supported freq:
>>>1, 10000000
>>>or:
>>>1, 1000
>>>
>>>freq set 10000000
>>>freq set 1
>>>
>>>Simple and easy.
>>>
>>
>>AFAIR, we wanted to have most commonly used frequencies as enums +
>>custom_freq
>>for some exotic ones (please note that there is also possible 2PPS, which
>>is
>>0.5 Hz).
>
>In this exotic case, user might add divider netlink attribute to divide
>the frequency pass in the attr. No problem.
>
>
>>This was design decision we already agreed on.
>>The userspace shall get definite list of comonly used frequencies that can
>>be
>>used with given HW, it clearly enums are good for this.
>
>I don't see why. Each instance supports a set of frequencies. It would
>pass the values to the userspace.
>
>I fail to see the need to have some fixed values listed in enums. Mixing
>approaches for a single attribute is wrong. In ethtool we also don't
>have enum values for 10,100,1000mbits etc. It's just a number.
>

In ethtool there are defines for linkspeeds.
There must be list of defines/enums to check the driver if it is supported.
Especially for ANY_FREQ we don't want to call driver 25 milions times or more.

Also, we have to move supported frequencies to the dpll_pin_alloc as it is
constant argument, supported frequencies shall not change @ runtime?
In such case there seems to be only one way to pass in a nice way, as a
bitmask?

Back to the userspace part, do you suggest to have DPLLA_PIN_FREQ attribute
and translate kernelspace enum values to userspace defines like 
DPLL_FREQ_1_HZ, etc? also with special define for supported ones ANY_FREQ?

>
>>
>>>
>>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value
>>>>defined
>>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>>+ **/
>>>>+enum dpll_pin_signal_type {
>>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>>+
>>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>>+};
>>>>+
>>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>>+
>>>>+/* dpll_pin_mode - available pin states
>>>>+ *
>>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>>>>+ **/
>>>>+enum dpll_pin_mode {
>>>>+	DPLL_PIN_MODE_UNSPEC,
>>>>+	DPLL_PIN_MODE_CONNECTED,
>>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>>+	DPLL_PIN_MODE_SOURCE,
>>>>+	DPLL_PIN_MODE_OUTPUT,
>>>
>>>I don't follow. I see 2 enums:
>>>CONNECTED/DISCONNECTED
>>>SOURCE/OUTPUT
>>>why this is mangled together? How is it supposed to be working. Like a
>>>bitarray?
>>>
>>
>>The userspace shouldn't worry about bits, it recieves a list of
>attributes.
>>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>>DPLLA_PIN_MODE_SUPPORTED. I.e.
>>
>>	DPLLA_PIN_IDX			0
>>	DPLLA_PIN_MODE			1,3
>>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4
>
>I believe that mixing apples and oranges in a single attr is not correct.
>Could you please split to separate attrs as drafted below?
>
>>
>>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>>and bitmask is not a way to go for userspace.
>
>What? See nla_bitmap.
>

AFAIK, nla_bitmap is not yet merged.

>Anyway, why can't you have:
>DPLLA_PIN_CONNECTED     u8 1/0 (bool)
>DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }

Don't get it, why this shall be u8 with bool value, doesn't make much sense for
userspace.
All the other attributes have enum type, we can go with separated attribute:
DPLLA_PIN_STATE		enum { CONNECTED/DISCONNECTED }
Just be consistent and clear, and yes u8 is enough it to keep it, as well as
all of attribute enum values, so we can use u8 instead of u32 for all of them.

Actually for "connected/disconnected"-part there are 2 valid use-cases on my
mind:
- pin can be connected with a number of "parents" (dplls or muxed-pins)
- pin is disconnected entirely
Second case can be achieved with control over first one, thus not need for any
special approach here. Proper control would be to let userspace connect or
disconnect a pin per each node it can be connected with, right?

Then example dump of "get-pins" could look like this:
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		0
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_EXT
	DPLLA_PIN_DIRECTION	SOURCE
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		CONNECTED
	DPLLA_DPLL			(nested)
		DPLLA_ID		1
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED

DPLL_PIN	(nested)
	DPLLA_PIN_IDX		1
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
	DPLLA_PIN_DIRECTION	SOURCE
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED
	DPLLA_DPLL			(nested)
		DPLLA_ID		1
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		CONNECTED

DPLL_PIN	(nested)	
	DPLLA_PIN_IDX		2
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
	DPLLA_PIN_DIRECTION	SOURCE
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED
	DPLLA_DPLL			(nested)
		DPLLA_ID		1
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED

(similar for muxed pins)
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		3
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
	DPLLA_PIN_DIRECTION	SOURCE
	DPLLA_PIN_PARENT		(nested)
		DPLLA_PIN_IDX		1
		DPLLA_PIN_STATE		DISCONNECTED
	DPLLA_PIN_PARENT		(nested)
		DPLLA_PIN_IDX		2
		DPLLA_PIN_STATE		CONNECTED
			
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		4
	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
	DPLLA_PIN_DIRECTION	SOURCE
	DPLLA_PIN_PARENT		(nested)
		DPLLA_PIN_IDX		1
		DPLLA_PIN_STATE		CONNECTED
	DPLLA_PIN_PARENT		(nested)
		DPLLA_PIN_IDX		2
		DPLLA_PIN_STATE		DISCONNECTED

For DPLL_MODE_MANUAL a DPLLA_PIN_STATE would serve also as signal selector
mechanism.
In above example DPLL_ID=0 has only "connected" DPLL_PIN_IDX=0, now when
different pin "connect" is requested:

dpll-set request:
DPLLA_DPLL	(nested)
	DPLLA_ID=0
	DPLLA_NAME=pci_0000:00:00.0
DPLLA_PIN
	DPLLA_PIN_IDX=2
	DPLLA_PIN_CONNECTED=1

Former shall "disconnect"..
And now, dump pin-get:
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		0
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		DISCONNECTED
...
DPLL_PIN	(nested)
	DPLLA_PIN_IDX		2
	...
	DPLLA_DPLL			(nested)
		DPLLA_ID		0
		DPLLA_NAME		pci_0000:00:00.0
		DPLLA_PIN_STATE		CONNECTED
		
At least that shall happen on hardware level, right?

As I can't find a use-case to have a pin "connected" but not "selected" in case
of DPLL_MODE_MANUAL.

A bit different is with DPLL_MODE_AUTOMATIC, the pins that connects with dpll
directly could be all connected, and their selection is auto-controlled with a
DPLLA_PIN_PRIO.
But still the user may also request to disconnect a pin - not use it at all
(instead of configuring lowest priority - which allows to use it, if all other
pins propagate invalid signal).

Thus, for DPLL_MODE_AUTOMATIC all ablove is the same with a one difference,
each pin/dpll pair would have a prio, like suggested in the other email.
DPLLA_PIN	(nested)
	...
	DPLLA_DPLL	(nested)
		...
		DPLLA_PIN_CONNECTED	<connected value>
		DPLLA_PIN_STATE		<prio value>

Which basically means that both DPLL_A_PIN_PRIO and DPLLA_PIN_STATE
shall be a property of a PIN-DPLL pair, and configured as such.


>DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED,
>CAN_CHANGE_DIRECTION)
>
>We can use the capabilitis bitfield eventually for other purposes as
>well, it is going to be handy I'm sure.
>

Well, in general I like the idea, altough the details...
We have 3 configuration levels:
- DPLL
- DPLL/PIN
- PIN

Considering that, there is space for 3 of such CAPABILITIES attributes, but:
- DPLL can only configure MODE for now, so we can only convert
DPLL_A_MODE_SUPPORTED to a bitfield, and add DPLL_CAPS later if required
- DPLL/PIN pair has configurable DPLLA_PIN_PRIO and DPLLA_PIN_STATE, so we
could introduce DPLLA_PIN_DPLL_CAPS for them
- PIN has now configurable frequency (but this is done by providing list of
supported ones - no need for extra attribute). We already know that pin shall
also have optional features, like phase offset, embedded sync.
For embedded sync if supported it shall also be a set of supported frequencies.
Possibly for phase offset we could use similar CAPS field, but don't think will
manage this into next version.

>
>
>>
>>
>>>
>>>>+
>>>>+	__DPLL_PIN_MODE_MAX,
>>>>+};
>>>>+
>
>[...]
>
>
>>>>+/**
>>>>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate
>>>>>how
>>>>+ * dpll selects one of its sources to syntonize with a source.
>>>>+ *
>>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request
>>>>to dpll
>>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>>>>dpll
>>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover
>>>>available
>>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>>>
>>>Why does the user care which oscilator is run internally. It's freerun,
>>>isn't it? If you want to expose oscilator type, you should do it
>>>elsewhere.
>>>
>>
>>In NCO user might change frequency of an output, in freerun cannot.
>
>How this could be done?
>

I guess by some internal synchronizer frequency dividers. Same as other output
(different then input) frequencies are achievable there.

Thanks,
Arkadiusz

>
>[...]


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-02-06  2:00           ` Kubalewski, Arkadiusz
@ 2023-02-07 14:15             ` Jiri Pirko
  -1 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-02-07 14:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

Mon, Feb 06, 2023 at 03:00:09AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, January 31, 2023 3:01 PM
>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>Cc: Vadim Fedorenko <vadfed@meta.com>; Jakub Kicinski <kuba@kernel.org>;
>>Jonathan Lemon <jonathan.lemon@gmail.com>; Paolo Abeni <pabeni@redhat.com>;
>>netdev@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
>>clk@vger.kernel.org; Olech, Milena <milena.olech@intel.com>; Michalik,
>>Michal <michal.michalik@intel.com>
>>Subject: Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
>>
>>Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Thursday, January 19, 2023 6:16 PM
>>>>
>>>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:

[...]


>>>>>+			 struct dpll_pin_ops *ops, void *priv)
>>>>>+{
>>>>>+	struct dpll_pin *pin;
>>>>>+	int ret;
>>>>>+
>>>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>>>+					  shared_pin_description);
>>>>>+	if (!pin) {
>>>>>+		ret = -EINVAL;
>>>>>+		goto unlock;
>>>>>+	}
>>>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>>>+unlock:
>>>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>>>+
>>>>>+	return ret;
>>>>
>>>>I don't understand why there should be a separate function to register
>>>>the shared pin. As I see it, there is a pin object that could be
>>>>registered with 2 or more dpll devices. What about having:
>>>>
>>>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>>>dpll_pin_register(dpll_1, pin);
>>>>dpll_pin_register(dpll_2, pin);
>>>>dpll_pin_register(dpll_3, pin);
>>>>
>>>
>>>IMHO your example works already, but it would possible only if the same
>>>driver
>>>instance initializes all dplls.
>>
>>It should be only one instance of dpll to be shared between driver
>>instances as I wrote in the reply to the "ice" part. There might he some
>>pins created alongside with this.
>>
>
>pin = dpll_pin_alloc(desc, type, ops, priv)
>dpll_pin_register(dpll_1, pin);
>dpll_pin_register(dpll_2, pin);
>dpll_pin_register(dpll_3, pin);
>^ there is registration of a single pin by a 3 dpll instances, and a kernel
>module instance which registers them has a reference to the pin and all dplls,
>thus it can just register them all without any problems, don't need to call
>dpll_shared_pin_register(..).
>
>Now imagine 2 kernel module instances.
>One (#1) creates one dpll, registers pins with it.
>Second (#2) creates second dpll, and want to use/register pins of dpll
>registered by the first instance (#1).

Sure, both instances should be available to both module instances, using
the suggested get/put create/reference system.
Whichever module instance does register shared pin can use
dpll_pin_register(), I see no problem with that.


>
>>My point is, the first driver instance which creates dpll registers also
>>the pins. The other driver instance does not do anything, just gets
>>reference to the dpll.
>>
>>On cleanup path, the last driver instance tearing down would unregister
>>dpll pins (Could be done automatically by dpll_device_put()).
>>
>>There might be some other pins (Synce) created per driver instance
>>(per-PF). You have to distinguish these 2 groups.
>>
>>
>>>dpll_shared_pin_register is designed for driver instances without the pin
>>
>>I think we need to make sure the terms are correct "sharing" is between
>>multiple dpll instances. However, if 2 driver instances are sharing the
>>same dpll instance, this instance has pins. There is no sharing unless
>>there is another dpll instance in picture. Correct?
>>
>
>Yes!
>If two kernel module intances sharing a dpll instance, the pins belong
>to the dpll instance, and yes each kernel module instance can register pins
>with that dpll instance just with: dpll_pin_register(dpll_1, pin);
>
>dpll_shared_pin_register(..) shall be used when separated kernel module
>instances are initializing separated dpll instances, and those instances are

Why exacly would they do that? Could you please draw me an example?


>physically sharing their pins.
>
>>

[...]


>>>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>>>+				   const struct dpll_device *dpll,
>>>>>+				   const struct dpll_pin *pin)
>>>>>+{
>>>>>+	enum dpll_pin_mode i;
>>>>>+	bool active;
>>>>>+
>>>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>>>+			return 0;
>>>>>+		if (active)
>>>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>>>
>>>>Why this is signed?
>>>>
>>>
>>>Because enums are signed.
>>
>>You use negative values in enums? Don't do that here. Have all netlink
>>atrributes unsigned please.
>>
>
>No, we don't use negative values, but enum is a signed type by itself.
>Doesn't seem right thing to do, put signed-type value into unsigned type TLV.
>This smells very bad.

Well, then all existing uses that carry enum over netlink attributes
smell bad. The enum values are all unsigned, I see no reason to use S*.
Please be consistent with the rest of the Netlink uAPI.


[...]

>>>>>+
>>>>>+/* dpll_pin_signal_type - signal types
>>>>>+ *
>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>>>
>>>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
>>>>with HZ value directly? For example, supported freq:
>>>>1, 10000000
>>>>or:
>>>>1, 1000
>>>>
>>>>freq set 10000000
>>>>freq set 1
>>>>
>>>>Simple and easy.
>>>>
>>>
>>>AFAIR, we wanted to have most commonly used frequencies as enums +
>>>custom_freq
>>>for some exotic ones (please note that there is also possible 2PPS, which
>>>is
>>>0.5 Hz).
>>
>>In this exotic case, user might add divider netlink attribute to divide
>>the frequency pass in the attr. No problem.
>>
>>
>>>This was design decision we already agreed on.
>>>The userspace shall get definite list of comonly used frequencies that can
>>>be
>>>used with given HW, it clearly enums are good for this.
>>
>>I don't see why. Each instance supports a set of frequencies. It would
>>pass the values to the userspace.
>>
>>I fail to see the need to have some fixed values listed in enums. Mixing
>>approaches for a single attribute is wrong. In ethtool we also don't
>>have enum values for 10,100,1000mbits etc. It's just a number.
>>
>
>In ethtool there are defines for linkspeeds.
>There must be list of defines/enums to check the driver if it is supported.
>Especially for ANY_FREQ we don't want to call driver 25 milions times or more.

Any is not really *any* is it? A simple range wouldn't do then? It would
be much better to tell the user the boundaries.


>
>Also, we have to move supported frequencies to the dpll_pin_alloc as it is
>constant argument, supported frequencies shall not change @ runtime?
>In such case there seems to be only one way to pass in a nice way, as a
>bitmask?

array of numbers (perhaps using defines for most common values), I don't
see any problem in that. But you are talking about in-kernel API. Does
not matter that much. What we are discussing is uAPI and that matters a
lot.


>
>Back to the userspace part, do you suggest to have DPLLA_PIN_FREQ attribute
>and translate kernelspace enum values to userspace defines like 
>DPLL_FREQ_1_HZ, etc? also with special define for supported ones ANY_FREQ?

Whichever is convenient. My focus here is uAPI.


>
>>
>>>
>>>>
>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value
>>>>>defined
>>>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>>>+ **/
>>>>>+enum dpll_pin_signal_type {
>>>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>>>+
>>>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>>>+};
>>>>>+
>>>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>>>+
>>>>>+/* dpll_pin_mode - available pin states
>>>>>+ *
>>>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>>>>>+ **/
>>>>>+enum dpll_pin_mode {
>>>>>+	DPLL_PIN_MODE_UNSPEC,
>>>>>+	DPLL_PIN_MODE_CONNECTED,
>>>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>>>+	DPLL_PIN_MODE_SOURCE,
>>>>>+	DPLL_PIN_MODE_OUTPUT,
>>>>
>>>>I don't follow. I see 2 enums:
>>>>CONNECTED/DISCONNECTED
>>>>SOURCE/OUTPUT
>>>>why this is mangled together? How is it supposed to be working. Like a
>>>>bitarray?
>>>>
>>>
>>>The userspace shouldn't worry about bits, it recieves a list of
>>attributes.
>>>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>>>DPLLA_PIN_MODE_SUPPORTED. I.e.
>>>
>>>	DPLLA_PIN_IDX			0
>>>	DPLLA_PIN_MODE			1,3
>>>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4
>>
>>I believe that mixing apples and oranges in a single attr is not correct.
>>Could you please split to separate attrs as drafted below?
>>
>>>
>>>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>>>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>>>and bitmask is not a way to go for userspace.
>>
>>What? See nla_bitmap.
>>
>
>AFAIK, nla_bitmap is not yet merged.

NLA_BITFIELD32


>
>>Anyway, why can't you have:
>>DPLLA_PIN_CONNECTED     u8 1/0 (bool)
>>DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }
>
>Don't get it, why this shall be u8 with bool value, doesn't make much sense for
>userspace.

Could be NLA_FLAG.


>All the other attributes have enum type, we can go with separated attribute:
>DPLLA_PIN_STATE		enum { CONNECTED/DISCONNECTED }

Yeah, why not. I think this is probably better and more explicit than
NLA_FLAG.


>Just be consistent and clear, and yes u8 is enough it to keep it, as well as
>all of attribute enum values, so we can use u8 instead of u32 for all of them.

Yes, that is what is done normally for attrs like this.


>
>Actually for "connected/disconnected"-part there are 2 valid use-cases on my
>mind:
>- pin can be connected with a number of "parents" (dplls or muxed-pins)
>- pin is disconnected entirely
>Second case can be achieved with control over first one, thus not need for any
>special approach here. Proper control would be to let userspace connect or
>disconnect a pin per each node it can be connected with, right?
>
>Then example dump of "get-pins" could look like this:
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		0
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_EXT
>	DPLLA_PIN_DIRECTION	SOURCE
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0

Nit, make sure you have this as 2 attrs, busname, devname.


>		DPLLA_PIN_STATE		CONNECTED
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		1
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED
>
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		1
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>	DPLLA_PIN_DIRECTION	SOURCE
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		1
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		CONNECTED
>
>DPLL_PIN	(nested)	
>	DPLLA_PIN_IDX		2
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>	DPLLA_PIN_DIRECTION	SOURCE
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		1
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED

Okay.


>
>(similar for muxed pins)
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		3
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>	DPLLA_PIN_DIRECTION	SOURCE
>	DPLLA_PIN_PARENT		(nested)
>		DPLLA_PIN_IDX		1
>		DPLLA_PIN_STATE		DISCONNECTED
>	DPLLA_PIN_PARENT		(nested)
>		DPLLA_PIN_IDX		2
>		DPLLA_PIN_STATE		CONNECTED
>			
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		4
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>	DPLLA_PIN_DIRECTION	SOURCE
>	DPLLA_PIN_PARENT		(nested)
>		DPLLA_PIN_IDX		1
>		DPLLA_PIN_STATE		CONNECTED
>	DPLLA_PIN_PARENT		(nested)
>		DPLLA_PIN_IDX		2
>		DPLLA_PIN_STATE		DISCONNECTED

Looks fine.


>
>For DPLL_MODE_MANUAL a DPLLA_PIN_STATE would serve also as signal selector
>mechanism.

Yep, I already make this point in earlier rfc review comment.


>In above example DPLL_ID=0 has only "connected" DPLL_PIN_IDX=0, now when
>different pin "connect" is requested:
>
>dpll-set request:
>DPLLA_DPLL	(nested)
>	DPLLA_ID=0
>	DPLLA_NAME=pci_0000:00:00.0
>DPLLA_PIN
>	DPLLA_PIN_IDX=2
>	DPLLA_PIN_CONNECTED=1
>
>Former shall "disconnect"..
>And now, dump pin-get:
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		0
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED
>...
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		2
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		CONNECTED
>		
>At least that shall happen on hardware level, right?
>
>As I can't find a use-case to have a pin "connected" but not "selected" in case
>of DPLL_MODE_MANUAL.

Exactly.


>
>A bit different is with DPLL_MODE_AUTOMATIC, the pins that connects with dpll
>directly could be all connected, and their selection is auto-controlled with a
>DPLLA_PIN_PRIO.
>But still the user may also request to disconnect a pin - not use it at all
>(instead of configuring lowest priority - which allows to use it, if all other
>pins propagate invalid signal).
>
>Thus, for DPLL_MODE_AUTOMATIC all ablove is the same with a one difference,
>each pin/dpll pair would have a prio, like suggested in the other email.
>DPLLA_PIN	(nested)
>	...
>	DPLLA_DPLL	(nested)
>		...
>		DPLLA_PIN_CONNECTED	<connected value>
>		DPLLA_PIN_STATE		<prio value>

I think you made a mistake. Should it be:
		DPLLA_PIN_STATE		<connected value>
		DPLLA_PIN_PRIO		<prio value>
?


>
>Which basically means that both DPLL_A_PIN_PRIO and DPLLA_PIN_STATE
>shall be a property of a PIN-DPLL pair, and configured as such.

Yes.


>
>
>>DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED,
>>CAN_CHANGE_DIRECTION)
>>
>>We can use the capabilitis bitfield eventually for other purposes as
>>well, it is going to be handy I'm sure.
>>
>
>Well, in general I like the idea, altough the details...
>We have 3 configuration levels:
>- DPLL
>- DPLL/PIN
>- PIN
>
>Considering that, there is space for 3 of such CAPABILITIES attributes, but:
>- DPLL can only configure MODE for now, so we can only convert
>DPLL_A_MODE_SUPPORTED to a bitfield, and add DPLL_CAPS later if required

Can't do that. It's uAPI, once you have ATTR there, it's there for
eternity...


>- DPLL/PIN pair has configurable DPLLA_PIN_PRIO and DPLLA_PIN_STATE, so we
>could introduce DPLLA_PIN_DPLL_CAPS for them

Yeah.


>- PIN has now configurable frequency (but this is done by providing list of
>supported ones - no need for extra attribute). We already know that pin shall
>also have optional features, like phase offset, embedded sync.
>For embedded sync if supported it shall also be a set of supported frequencies.
>Possibly for phase offset we could use similar CAPS field, but don't think will
>manage this into next version.
>
>>
>>
>>>
>>>
>>>>
>>>>>+
>>>>>+	__DPLL_PIN_MODE_MAX,
>>>>>+};
>>>>>+
>>
>>[...]
>>
>>
>>>>>+/**
>>>>>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate
>>>>>>how
>>>>>+ * dpll selects one of its sources to syntonize with a source.
>>>>>+ *
>>>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request
>>>>>to dpll
>>>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>>>>>dpll
>>>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover
>>>>>available
>>>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>>>>
>>>>Why does the user care which oscilator is run internally. It's freerun,
>>>>isn't it? If you want to expose oscilator type, you should do it
>>>>elsewhere.
>>>>
>>>
>>>In NCO user might change frequency of an output, in freerun cannot.
>>
>>How this could be done?
>>
>
>I guess by some internal synchronizer frequency dividers. Same as other output
>(different then input) frequencies are achievable there.

I ment uAPI wise. Speak Netlink.


>
>Thanks,
>Arkadiusz
>
>>
>>[...]
>

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-02-07 14:15             ` Jiri Pirko
  0 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-02-07 14:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

Mon, Feb 06, 2023 at 03:00:09AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, January 31, 2023 3:01 PM
>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>Cc: Vadim Fedorenko <vadfed@meta.com>; Jakub Kicinski <kuba@kernel.org>;
>>Jonathan Lemon <jonathan.lemon@gmail.com>; Paolo Abeni <pabeni@redhat.com>;
>>netdev@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
>>clk@vger.kernel.org; Olech, Milena <milena.olech@intel.com>; Michalik,
>>Michal <michal.michalik@intel.com>
>>Subject: Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
>>
>>Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Thursday, January 19, 2023 6:16 PM
>>>>
>>>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:

[...]


>>>>>+			 struct dpll_pin_ops *ops, void *priv)
>>>>>+{
>>>>>+	struct dpll_pin *pin;
>>>>>+	int ret;
>>>>>+
>>>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>>>+					  shared_pin_description);
>>>>>+	if (!pin) {
>>>>>+		ret = -EINVAL;
>>>>>+		goto unlock;
>>>>>+	}
>>>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>>>+unlock:
>>>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>>>+
>>>>>+	return ret;
>>>>
>>>>I don't understand why there should be a separate function to register
>>>>the shared pin. As I see it, there is a pin object that could be
>>>>registered with 2 or more dpll devices. What about having:
>>>>
>>>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>>>dpll_pin_register(dpll_1, pin);
>>>>dpll_pin_register(dpll_2, pin);
>>>>dpll_pin_register(dpll_3, pin);
>>>>
>>>
>>>IMHO your example works already, but it would possible only if the same
>>>driver
>>>instance initializes all dplls.
>>
>>It should be only one instance of dpll to be shared between driver
>>instances as I wrote in the reply to the "ice" part. There might he some
>>pins created alongside with this.
>>
>
>pin = dpll_pin_alloc(desc, type, ops, priv)
>dpll_pin_register(dpll_1, pin);
>dpll_pin_register(dpll_2, pin);
>dpll_pin_register(dpll_3, pin);
>^ there is registration of a single pin by a 3 dpll instances, and a kernel
>module instance which registers them has a reference to the pin and all dplls,
>thus it can just register them all without any problems, don't need to call
>dpll_shared_pin_register(..).
>
>Now imagine 2 kernel module instances.
>One (#1) creates one dpll, registers pins with it.
>Second (#2) creates second dpll, and want to use/register pins of dpll
>registered by the first instance (#1).

Sure, both instances should be available to both module instances, using
the suggested get/put create/reference system.
Whichever module instance does register shared pin can use
dpll_pin_register(), I see no problem with that.


>
>>My point is, the first driver instance which creates dpll registers also
>>the pins. The other driver instance does not do anything, just gets
>>reference to the dpll.
>>
>>On cleanup path, the last driver instance tearing down would unregister
>>dpll pins (Could be done automatically by dpll_device_put()).
>>
>>There might be some other pins (Synce) created per driver instance
>>(per-PF). You have to distinguish these 2 groups.
>>
>>
>>>dpll_shared_pin_register is designed for driver instances without the pin
>>
>>I think we need to make sure the terms are correct "sharing" is between
>>multiple dpll instances. However, if 2 driver instances are sharing the
>>same dpll instance, this instance has pins. There is no sharing unless
>>there is another dpll instance in picture. Correct?
>>
>
>Yes!
>If two kernel module intances sharing a dpll instance, the pins belong
>to the dpll instance, and yes each kernel module instance can register pins
>with that dpll instance just with: dpll_pin_register(dpll_1, pin);
>
>dpll_shared_pin_register(..) shall be used when separated kernel module
>instances are initializing separated dpll instances, and those instances are

Why exacly would they do that? Could you please draw me an example?


>physically sharing their pins.
>
>>

[...]


>>>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>>>+				   const struct dpll_device *dpll,
>>>>>+				   const struct dpll_pin *pin)
>>>>>+{
>>>>>+	enum dpll_pin_mode i;
>>>>>+	bool active;
>>>>>+
>>>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>>>+			return 0;
>>>>>+		if (active)
>>>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>>>
>>>>Why this is signed?
>>>>
>>>
>>>Because enums are signed.
>>
>>You use negative values in enums? Don't do that here. Have all netlink
>>atrributes unsigned please.
>>
>
>No, we don't use negative values, but enum is a signed type by itself.
>Doesn't seem right thing to do, put signed-type value into unsigned type TLV.
>This smells very bad.

Well, then all existing uses that carry enum over netlink attributes
smell bad. The enum values are all unsigned, I see no reason to use S*.
Please be consistent with the rest of the Netlink uAPI.


[...]

>>>>>+
>>>>>+/* dpll_pin_signal_type - signal types
>>>>>+ *
>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>>>
>>>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we work
>>>>with HZ value directly? For example, supported freq:
>>>>1, 10000000
>>>>or:
>>>>1, 1000
>>>>
>>>>freq set 10000000
>>>>freq set 1
>>>>
>>>>Simple and easy.
>>>>
>>>
>>>AFAIR, we wanted to have most commonly used frequencies as enums +
>>>custom_freq
>>>for some exotic ones (please note that there is also possible 2PPS, which
>>>is
>>>0.5 Hz).
>>
>>In this exotic case, user might add divider netlink attribute to divide
>>the frequency pass in the attr. No problem.
>>
>>
>>>This was design decision we already agreed on.
>>>The userspace shall get definite list of comonly used frequencies that can
>>>be
>>>used with given HW, it clearly enums are good for this.
>>
>>I don't see why. Each instance supports a set of frequencies. It would
>>pass the values to the userspace.
>>
>>I fail to see the need to have some fixed values listed in enums. Mixing
>>approaches for a single attribute is wrong. In ethtool we also don't
>>have enum values for 10,100,1000mbits etc. It's just a number.
>>
>
>In ethtool there are defines for linkspeeds.
>There must be list of defines/enums to check the driver if it is supported.
>Especially for ANY_FREQ we don't want to call driver 25 milions times or more.

Any is not really *any* is it? A simple range wouldn't do then? It would
be much better to tell the user the boundaries.


>
>Also, we have to move supported frequencies to the dpll_pin_alloc as it is
>constant argument, supported frequencies shall not change @ runtime?
>In such case there seems to be only one way to pass in a nice way, as a
>bitmask?

array of numbers (perhaps using defines for most common values), I don't
see any problem in that. But you are talking about in-kernel API. Does
not matter that much. What we are discussing is uAPI and that matters a
lot.


>
>Back to the userspace part, do you suggest to have DPLLA_PIN_FREQ attribute
>and translate kernelspace enum values to userspace defines like 
>DPLL_FREQ_1_HZ, etc? also with special define for supported ones ANY_FREQ?

Whichever is convenient. My focus here is uAPI.


>
>>
>>>
>>>>
>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal, value
>>>>>defined
>>>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>>>+ **/
>>>>>+enum dpll_pin_signal_type {
>>>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>>>+
>>>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>>>+};
>>>>>+
>>>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>>>+
>>>>>+/* dpll_pin_mode - available pin states
>>>>>+ *
>>>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin
>>>>>+ **/
>>>>>+enum dpll_pin_mode {
>>>>>+	DPLL_PIN_MODE_UNSPEC,
>>>>>+	DPLL_PIN_MODE_CONNECTED,
>>>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>>>+	DPLL_PIN_MODE_SOURCE,
>>>>>+	DPLL_PIN_MODE_OUTPUT,
>>>>
>>>>I don't follow. I see 2 enums:
>>>>CONNECTED/DISCONNECTED
>>>>SOURCE/OUTPUT
>>>>why this is mangled together? How is it supposed to be working. Like a
>>>>bitarray?
>>>>
>>>
>>>The userspace shouldn't worry about bits, it recieves a list of
>>attributes.
>>>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>>>DPLLA_PIN_MODE_SUPPORTED. I.e.
>>>
>>>	DPLLA_PIN_IDX			0
>>>	DPLLA_PIN_MODE			1,3
>>>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4
>>
>>I believe that mixing apples and oranges in a single attr is not correct.
>>Could you please split to separate attrs as drafted below?
>>
>>>
>>>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>>>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>>>and bitmask is not a way to go for userspace.
>>
>>What? See nla_bitmap.
>>
>
>AFAIK, nla_bitmap is not yet merged.

NLA_BITFIELD32


>
>>Anyway, why can't you have:
>>DPLLA_PIN_CONNECTED     u8 1/0 (bool)
>>DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }
>
>Don't get it, why this shall be u8 with bool value, doesn't make much sense for
>userspace.

Could be NLA_FLAG.


>All the other attributes have enum type, we can go with separated attribute:
>DPLLA_PIN_STATE		enum { CONNECTED/DISCONNECTED }

Yeah, why not. I think this is probably better and more explicit than
NLA_FLAG.


>Just be consistent and clear, and yes u8 is enough it to keep it, as well as
>all of attribute enum values, so we can use u8 instead of u32 for all of them.

Yes, that is what is done normally for attrs like this.


>
>Actually for "connected/disconnected"-part there are 2 valid use-cases on my
>mind:
>- pin can be connected with a number of "parents" (dplls or muxed-pins)
>- pin is disconnected entirely
>Second case can be achieved with control over first one, thus not need for any
>special approach here. Proper control would be to let userspace connect or
>disconnect a pin per each node it can be connected with, right?
>
>Then example dump of "get-pins" could look like this:
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		0
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_EXT
>	DPLLA_PIN_DIRECTION	SOURCE
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0

Nit, make sure you have this as 2 attrs, busname, devname.


>		DPLLA_PIN_STATE		CONNECTED
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		1
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED
>
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		1
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>	DPLLA_PIN_DIRECTION	SOURCE
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		1
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		CONNECTED
>
>DPLL_PIN	(nested)	
>	DPLLA_PIN_IDX		2
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>	DPLLA_PIN_DIRECTION	SOURCE
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		1
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED

Okay.


>
>(similar for muxed pins)
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		3
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>	DPLLA_PIN_DIRECTION	SOURCE
>	DPLLA_PIN_PARENT		(nested)
>		DPLLA_PIN_IDX		1
>		DPLLA_PIN_STATE		DISCONNECTED
>	DPLLA_PIN_PARENT		(nested)
>		DPLLA_PIN_IDX		2
>		DPLLA_PIN_STATE		CONNECTED
>			
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		4
>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>	DPLLA_PIN_DIRECTION	SOURCE
>	DPLLA_PIN_PARENT		(nested)
>		DPLLA_PIN_IDX		1
>		DPLLA_PIN_STATE		CONNECTED
>	DPLLA_PIN_PARENT		(nested)
>		DPLLA_PIN_IDX		2
>		DPLLA_PIN_STATE		DISCONNECTED

Looks fine.


>
>For DPLL_MODE_MANUAL a DPLLA_PIN_STATE would serve also as signal selector
>mechanism.

Yep, I already make this point in earlier rfc review comment.


>In above example DPLL_ID=0 has only "connected" DPLL_PIN_IDX=0, now when
>different pin "connect" is requested:
>
>dpll-set request:
>DPLLA_DPLL	(nested)
>	DPLLA_ID=0
>	DPLLA_NAME=pci_0000:00:00.0
>DPLLA_PIN
>	DPLLA_PIN_IDX=2
>	DPLLA_PIN_CONNECTED=1
>
>Former shall "disconnect"..
>And now, dump pin-get:
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		0
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		DISCONNECTED
>...
>DPLL_PIN	(nested)
>	DPLLA_PIN_IDX		2
>	...
>	DPLLA_DPLL			(nested)
>		DPLLA_ID		0
>		DPLLA_NAME		pci_0000:00:00.0
>		DPLLA_PIN_STATE		CONNECTED
>		
>At least that shall happen on hardware level, right?
>
>As I can't find a use-case to have a pin "connected" but not "selected" in case
>of DPLL_MODE_MANUAL.

Exactly.


>
>A bit different is with DPLL_MODE_AUTOMATIC, the pins that connects with dpll
>directly could be all connected, and their selection is auto-controlled with a
>DPLLA_PIN_PRIO.
>But still the user may also request to disconnect a pin - not use it at all
>(instead of configuring lowest priority - which allows to use it, if all other
>pins propagate invalid signal).
>
>Thus, for DPLL_MODE_AUTOMATIC all ablove is the same with a one difference,
>each pin/dpll pair would have a prio, like suggested in the other email.
>DPLLA_PIN	(nested)
>	...
>	DPLLA_DPLL	(nested)
>		...
>		DPLLA_PIN_CONNECTED	<connected value>
>		DPLLA_PIN_STATE		<prio value>

I think you made a mistake. Should it be:
		DPLLA_PIN_STATE		<connected value>
		DPLLA_PIN_PRIO		<prio value>
?


>
>Which basically means that both DPLL_A_PIN_PRIO and DPLLA_PIN_STATE
>shall be a property of a PIN-DPLL pair, and configured as such.

Yes.


>
>
>>DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED,
>>CAN_CHANGE_DIRECTION)
>>
>>We can use the capabilitis bitfield eventually for other purposes as
>>well, it is going to be handy I'm sure.
>>
>
>Well, in general I like the idea, altough the details...
>We have 3 configuration levels:
>- DPLL
>- DPLL/PIN
>- PIN
>
>Considering that, there is space for 3 of such CAPABILITIES attributes, but:
>- DPLL can only configure MODE for now, so we can only convert
>DPLL_A_MODE_SUPPORTED to a bitfield, and add DPLL_CAPS later if required

Can't do that. It's uAPI, once you have ATTR there, it's there for
eternity...


>- DPLL/PIN pair has configurable DPLLA_PIN_PRIO and DPLLA_PIN_STATE, so we
>could introduce DPLLA_PIN_DPLL_CAPS for them

Yeah.


>- PIN has now configurable frequency (but this is done by providing list of
>supported ones - no need for extra attribute). We already know that pin shall
>also have optional features, like phase offset, embedded sync.
>For embedded sync if supported it shall also be a set of supported frequencies.
>Possibly for phase offset we could use similar CAPS field, but don't think will
>manage this into next version.
>
>>
>>
>>>
>>>
>>>>
>>>>>+
>>>>>+	__DPLL_PIN_MODE_MAX,
>>>>>+};
>>>>>+
>>
>>[...]
>>
>>
>>>>>+/**
>>>>>+ * dpll_mode - Working-modes a dpll can support. Modes differentiate
>>>>>>how
>>>>>+ * dpll selects one of its sources to syntonize with a source.
>>>>>+ *
>>>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a request
>>>>>to dpll
>>>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>>>>>dpll
>>>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover
>>>>>available
>>>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>>>>
>>>>Why does the user care which oscilator is run internally. It's freerun,
>>>>isn't it? If you want to expose oscilator type, you should do it
>>>>elsewhere.
>>>>
>>>
>>>In NCO user might change frequency of an output, in freerun cannot.
>>
>>How this could be done?
>>
>
>I guess by some internal synchronizer frequency dividers. Same as other output
>(different then input) frequencies are achievable there.

I ment uAPI wise. Speak Netlink.


>
>Thanks,
>Arkadiusz
>
>>
>>[...]
>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* RE: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-02-07 14:15             ` Jiri Pirko
@ 2023-03-07 12:23               ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-07 12:23 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, February 7, 2023 3:15 PM
>
>Mon, Feb 06, 2023 at 03:00:09AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Tuesday, January 31, 2023 3:01 PM
>>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>>Cc: Vadim Fedorenko <vadfed@meta.com>; Jakub Kicinski
>>><kuba@kernel.org>; Jonathan Lemon <jonathan.lemon@gmail.com>; Paolo
>>>Abeni <pabeni@redhat.com>; netdev@vger.kernel.org;
>>>linux-arm-kernel@lists.infradead.org; linux- clk@vger.kernel.org;
>>>Olech, Milena <milena.olech@intel.com>; Michalik, Michal
>>><michal.michalik@intel.com>
>>>Subject: Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base
>>>functions
>>>
>>>Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com
>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Thursday, January 19, 2023 6:16 PM
>>>>>
>>>>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>>>>>>+			 struct dpll_pin_ops *ops, void *priv) {
>>>>>>+	struct dpll_pin *pin;
>>>>>>+	int ret;
>>>>>>+
>>>>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>>>>+					  shared_pin_description);
>>>>>>+	if (!pin) {
>>>>>>+		ret = -EINVAL;
>>>>>>+		goto unlock;
>>>>>>+	}
>>>>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>>>>+unlock:
>>>>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>>>>+
>>>>>>+	return ret;
>>>>>
>>>>>I don't understand why there should be a separate function to
>>>>>register the shared pin. As I see it, there is a pin object that
>>>>>could be registered with 2 or more dpll devices. What about having:
>>>>>
>>>>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>>>>dpll_pin_register(dpll_1, pin); dpll_pin_register(dpll_2, pin);
>>>>>dpll_pin_register(dpll_3, pin);
>>>>>
>>>>
>>>>IMHO your example works already, but it would possible only if the
>>>>same driver instance initializes all dplls.
>>>
>>>It should be only one instance of dpll to be shared between driver
>>>instances as I wrote in the reply to the "ice" part. There might he
>>>some pins created alongside with this.
>>>
>>
>>pin = dpll_pin_alloc(desc, type, ops, priv) dpll_pin_register(dpll_1,
>>pin); dpll_pin_register(dpll_2, pin); dpll_pin_register(dpll_3, pin); ^
>>there is registration of a single pin by a 3 dpll instances, and a
>>kernel module instance which registers them has a reference to the pin
>>and all dplls, thus it can just register them all without any problems,
>>don't need to call dpll_shared_pin_register(..).
>>
>>Now imagine 2 kernel module instances.
>>One (#1) creates one dpll, registers pins with it.
>>Second (#2) creates second dpll, and want to use/register pins of dpll
>>registered by the first instance (#1).
>
>Sure, both instances should be available to both module instances, using
>the suggested get/put create/reference system.
>Whichever module instance does register shared pin can use
>dpll_pin_register(), I see no problem with that.
>

In v6 those suggestions are implemented.
AFAIK Vadim shall send it soon.

>
>>
>>>My point is, the first driver instance which creates dpll registers
>>>also the pins. The other driver instance does not do anything, just
>>>gets reference to the dpll.
>>>
>>>On cleanup path, the last driver instance tearing down would
>>>unregister dpll pins (Could be done automatically by dpll_device_put()).
>>>
>>>There might be some other pins (Synce) created per driver instance
>>>(per-PF). You have to distinguish these 2 groups.
>>>
>>>
>>>>dpll_shared_pin_register is designed for driver instances without the
>>>>pin
>>>
>>>I think we need to make sure the terms are correct "sharing" is
>>>between multiple dpll instances. However, if 2 driver instances are
>>>sharing the same dpll instance, this instance has pins. There is no
>>>sharing unless there is another dpll instance in picture. Correct?
>>>
>>
>>Yes!
>>If two kernel module intances sharing a dpll instance, the pins belong
>>to the dpll instance, and yes each kernel module instance can register
>>pins with that dpll instance just with: dpll_pin_register(dpll_1, pin);
>>
>>dpll_shared_pin_register(..) shall be used when separated kernel module
>>instances are initializing separated dpll instances, and those
>>instances are
>
>Why exacly would they do that? Could you please draw me an example?
>

I think we shall not follow this discussion as in v6 we already
have the mechanics you suggested, but sure:
+----------+                 
|i0 - GPS  |--------------\
+----------+              |
+----------+              |
|i1 - SMA1 |------------\ |
+----------+            | |
+----------+            | |
|i2 - SMA2 |----------\ | |
+----------+          | | |
                      | | |
+---------------------|-|-|-------------------------------------------+
| Channel A / FW0     | | |     +-------------+   +---+   +--------+  |
|                     | | |-i0--|Synchronizer0|---|   |---| PHY0.0 |--|
|         +---+       | | |     |             |   |   |   +--------+  |
| PHY0.0--|   |       | |---i1--|             |---| M |---| PHY0.1 |--|
|         |   |       | | |     | +-----+     |   | A |   +--------+  |
| PHY0.1--| M |       |-----i2--| |DPLL0|     |   | C |---| PHY0.2 |--|
|         | U |       | | |     | +-----+     |   | 0 |   +--------+  |
| PHY0.2--| X |--+----------i3--| +-----+     |---|   |---| ...    |--|
|         | 0 |  |    | | |     | |DPLL1|     |   |   |   +--------+  |
| ...   --|   |  | /--------i4--| +-----+     |---|   |---| PHY0.7 |--|
|         |   |  | |  | | |     +-------------+   +---+   +--------+  |
| PHY0.7--|   |  | |  | | |                                           |
|         +---+  | |  | | |                                           |
+----------------|-|--|-|-|-------------------------------------------+
| Channel B / FW1| |  | | |     +-------------+   +---+   +--------+  |
|                | |  | | \-i0--|Synchronizer1|---|   |---| PHY1.0 |--|
|         +---+  | |  | |       |             |   |   |   +--------+  |
| PHY1.0--|   |  | |  | \---i1--|             |---| M |---| PHY1.1 |--|
|         |   |  | |  |         | +-----+     |   | A |   +--------+  |
| PHY1.1--| M |  | |  \-----i2--| |DPLL0|     |   | C |---| PHY1.2 |--|
|         | U |  | |            | +-----+     |   | 1 |   +--------+  |
| PHY1.2--| X |  \-|--------i3--| +-----+     |---|   |---| ...    |--|
|         | 1 |    |            | |DPLL1|     |   |   |   +--------+  |
| ...   --|   |----+--------i4--| +-----+     |---|   |---| PHY1.7 |--|
|         |   |                 +-------------+   +---+   +--------+  |
| PHY1.7--|   |                                                       |
|         +---+                                                       |
+---------------------------------------------------------------------+

>
>>physically sharing their pins.
>>
>>>
>
>[...]
>
>
>>>>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>>>>+				   const struct dpll_device *dpll,
>>>>>>+				   const struct dpll_pin *pin) {
>>>>>>+	enum dpll_pin_mode i;
>>>>>>+	bool active;
>>>>>>+
>>>>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>>>>+			return 0;
>>>>>>+		if (active)
>>>>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>>>>
>>>>>Why this is signed?
>>>>>
>>>>
>>>>Because enums are signed.
>>>
>>>You use negative values in enums? Don't do that here. Have all netlink
>>>atrributes unsigned please.
>>>
>>
>>No, we don't use negative values, but enum is a signed type by itself.
>>Doesn't seem right thing to do, put signed-type value into unsigned type TLV.
>>This smells very bad.
>
>Well, then all existing uses that carry enum over netlink attributes smell
>bad. The enum values are all unsigned, I see no reason to use S*.
>Please be consistent with the rest of the Netlink uAPI.
>

Yes, exactly, don't know why to follow bad practicies, saying "that's how it's
done". Is there any reasoning behind this?

>
>[...]
>
>>>>>>+
>>>>>>+/* dpll_pin_signal_type - signal types
>>>>>>+ *
>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>>>>
>>>>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we
>>>>>work with HZ value directly? For example, supported freq:
>>>>>1, 10000000
>>>>>or:
>>>>>1, 1000
>>>>>
>>>>>freq set 10000000
>>>>>freq set 1
>>>>>
>>>>>Simple and easy.
>>>>>
>>>>
>>>>AFAIR, we wanted to have most commonly used frequencies as enums +
>>>>custom_freq for some exotic ones (please note that there is also
>>>>possible 2PPS, which is
>>>>0.5 Hz).
>>>
>>>In this exotic case, user might add divider netlink attribute to
>>>divide the frequency pass in the attr. No problem.
>>>
>>>
>>>>This was design decision we already agreed on.
>>>>The userspace shall get definite list of comonly used frequencies
>>>>that can be used with given HW, it clearly enums are good for this.
>>>
>>>I don't see why. Each instance supports a set of frequencies. It would
>>>pass the values to the userspace.
>>>
>>>I fail to see the need to have some fixed values listed in enums.
>>>Mixing approaches for a single attribute is wrong. In ethtool we also
>>>don't have enum values for 10,100,1000mbits etc. It's just a number.
>>>
>>
>>In ethtool there are defines for linkspeeds.
>>There must be list of defines/enums to check the driver if it is supported.
>>Especially for ANY_FREQ we don't want to call driver 25 milions times or
>>more.
>
>Any is not really *any* is it? A simple range wouldn't do then? It would be
>much better to tell the user the boundaries.
>

In v6 those suggestions are implemented.

>
>>
>>Also, we have to move supported frequencies to the dpll_pin_alloc as it
>>is constant argument, supported frequencies shall not change @ runtime?
>>In such case there seems to be only one way to pass in a nice way, as a
>>bitmask?
>
>array of numbers (perhaps using defines for most common values), I don't
>see any problem in that. But you are talking about in-kernel API. Does not
>matter that much. What we are discussing is uAPI and that matters a lot.
>
>
>>
>>Back to the userspace part, do you suggest to have DPLLA_PIN_FREQ
>>attribute and translate kernelspace enum values to userspace defines
>>like DPLL_FREQ_1_HZ, etc? also with special define for supported ones
>>ANY_FREQ?
>
>Whichever is convenient. My focus here is uAPI.
>

In v6 those suggestions are implemented.

>
>>
>>>
>>>>
>>>>>
>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal,
>>>>>>+ value
>>>>>>defined
>>>>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>>>>+ **/
>>>>>>+enum dpll_pin_signal_type {
>>>>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>>>>+
>>>>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>>>>+};
>>>>>>+
>>>>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>>>>+
>>>>>>+/* dpll_pin_mode - available pin states
>>>>>>+ *
>>>>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin  **/ enum
>>>>>>+dpll_pin_mode {
>>>>>>+	DPLL_PIN_MODE_UNSPEC,
>>>>>>+	DPLL_PIN_MODE_CONNECTED,
>>>>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>>>>+	DPLL_PIN_MODE_SOURCE,
>>>>>>+	DPLL_PIN_MODE_OUTPUT,
>>>>>
>>>>>I don't follow. I see 2 enums:
>>>>>CONNECTED/DISCONNECTED
>>>>>SOURCE/OUTPUT
>>>>>why this is mangled together? How is it supposed to be working. Like
>>>>>a bitarray?
>>>>>
>>>>
>>>>The userspace shouldn't worry about bits, it recieves a list of
>>>attributes.
>>>>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>>>>DPLLA_PIN_MODE_SUPPORTED. I.e.
>>>>
>>>>	DPLLA_PIN_IDX			0
>>>>	DPLLA_PIN_MODE			1,3
>>>>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4
>>>
>>>I believe that mixing apples and oranges in a single attr is not correct.
>>>Could you please split to separate attrs as drafted below?
>>>
>>>>
>>>>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>>>>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>>>>and bitmask is not a way to go for userspace.
>>>
>>>What? See nla_bitmap.
>>>
>>
>>AFAIK, nla_bitmap is not yet merged.
>
>NLA_BITFIELD32
>
>
>>
>>>Anyway, why can't you have:
>>>DPLLA_PIN_CONNECTED     u8 1/0 (bool)
>>>DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }
>>
>>Don't get it, why this shall be u8 with bool value, doesn't make much
>>sense for userspace.
>
>Could be NLA_FLAG.
>
>
>>All the other attributes have enum type, we can go with separated
>>attribute:
>>DPLLA_PIN_STATE		enum { CONNECTED/DISCONNECTED }
>
>Yeah, why not. I think this is probably better and more explicit than
>NLA_FLAG.
>
>
>>Just be consistent and clear, and yes u8 is enough it to keep it, as
>>well as all of attribute enum values, so we can use u8 instead of u32 for
>>all of them.
>
>Yes, that is what is done normally for attrs like this.
>
>

In v6, there are enums and attributes:
DPLL_A_PIN_STATE	enum { CONNECTED/DISCONNECTED }
DPLL_A_PIN_DIRECTION	enum { SOURCE/OUTPUT }

also new capabilities attributes DPLL_A_PIN_DPLL_CAPS
a bitmap - implicit from u32 value.

>>
>>Actually for "connected/disconnected"-part there are 2 valid use-cases
>>on my
>>mind:
>>- pin can be connected with a number of "parents" (dplls or muxed-pins)
>>- pin is disconnected entirely
>>Second case can be achieved with control over first one, thus not need
>>for any special approach here. Proper control would be to let userspace
>>connect or disconnect a pin per each node it can be connected with, right?
>>
>>Then example dump of "get-pins" could look like this:
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		0
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_EXT
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>
>Nit, make sure you have this as 2 attrs, busname, devname.

Sure.

>
>
>>		DPLLA_PIN_STATE		CONNECTED
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		1
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>>
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		1
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		1
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		CONNECTED
>>
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		2
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		1
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>
>Okay.
>
>
>>
>>(similar for muxed pins)
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		3
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	DPLLA_PIN_PARENT		(nested)
>>		DPLLA_PIN_IDX		1
>>		DPLLA_PIN_STATE		DISCONNECTED
>>	DPLLA_PIN_PARENT		(nested)
>>		DPLLA_PIN_IDX		2
>>		DPLLA_PIN_STATE		CONNECTED
>>
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		4
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	DPLLA_PIN_PARENT		(nested)
>>		DPLLA_PIN_IDX		1
>>		DPLLA_PIN_STATE		CONNECTED
>>	DPLLA_PIN_PARENT		(nested)
>>		DPLLA_PIN_IDX		2
>>		DPLLA_PIN_STATE		DISCONNECTED
>
>Looks fine.
>
>
>>
>>For DPLL_MODE_MANUAL a DPLLA_PIN_STATE would serve also as signal
>>selector mechanism.
>
>Yep, I already make this point in earlier rfc review comment.
>

Thanks for that :)

>
>>In above example DPLL_ID=0 has only "connected" DPLL_PIN_IDX=0, now
>>when different pin "connect" is requested:
>>
>>dpll-set request:
>>DPLLA_DPLL	(nested)
>>	DPLLA_ID=0
>>	DPLLA_NAME=pci_0000:00:00.0
>>DPLLA_PIN
>>	DPLLA_PIN_IDX=2
>>	DPLLA_PIN_CONNECTED=1
>>
>>Former shall "disconnect"..
>>And now, dump pin-get:
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		0
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>>...
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		2
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		CONNECTED
>>
>>At least that shall happen on hardware level, right?
>>
>>As I can't find a use-case to have a pin "connected" but not "selected"
>>in case of DPLL_MODE_MANUAL.
>
>Exactly.
>
>
>>
>>A bit different is with DPLL_MODE_AUTOMATIC, the pins that connects
>>with dpll directly could be all connected, and their selection is
>>auto-controlled with a DPLLA_PIN_PRIO.
>>But still the user may also request to disconnect a pin - not use it at
>>all (instead of configuring lowest priority - which allows to use it,
>>if all other pins propagate invalid signal).
>>
>>Thus, for DPLL_MODE_AUTOMATIC all ablove is the same with a one
>>difference, each pin/dpll pair would have a prio, like suggested in the
>other email.
>>DPLLA_PIN	(nested)
>>	...
>>	DPLLA_DPLL	(nested)
>>		...
>>		DPLLA_PIN_CONNECTED	<connected value>
>>		DPLLA_PIN_STATE		<prio value>
>
>I think you made a mistake. Should it be:
>		DPLLA_PIN_STATE		<connected value>
>		DPLLA_PIN_PRIO		<prio value>
>?
>

Yes, exactly.

>
>>
>>Which basically means that both DPLL_A_PIN_PRIO and DPLLA_PIN_STATE
>>shall be a property of a PIN-DPLL pair, and configured as such.
>
>Yes.
>
>
>>
>>
>>>DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED,
>>>CAN_CHANGE_DIRECTION)
>>>
>>>We can use the capabilitis bitfield eventually for other purposes as
>>>well, it is going to be handy I'm sure.
>>>
>>
>>Well, in general I like the idea, altough the details...
>>We have 3 configuration levels:
>>- DPLL
>>- DPLL/PIN
>>- PIN
>>
>>Considering that, there is space for 3 of such CAPABILITIES attributes, but:
>>- DPLL can only configure MODE for now, so we can only convert
>>DPLL_A_MODE_SUPPORTED to a bitfield, and add DPLL_CAPS later if
>>required
>
>Can't do that. It's uAPI, once you have ATTR there, it's there for
>eternity...
>

I am not saying to remove something but add in the future.

>
>>- DPLL/PIN pair has configurable DPLLA_PIN_PRIO and DPLLA_PIN_STATE, so
>>we could introduce DPLLA_PIN_DPLL_CAPS for them
>
>Yeah.
>
>
>>- PIN has now configurable frequency (but this is done by providing
>>list of supported ones - no need for extra attribute). We already know
>>that pin shall also have optional features, like phase offset, embedded
>>sync.
>>For embedded sync if supported it shall also be a set of supported
>>frequencies.
>>Possibly for phase offset we could use similar CAPS field, but don't
>>think will manage this into next version.
>>
>>>
>>>
>>>>
>>>>
>>>>>
>>>>>>+
>>>>>>+	__DPLL_PIN_MODE_MAX,
>>>>>>+};
>>>>>>+
>>>
>>>[...]
>>>
>>>
>>>>>>+/**
>>>>>>+ * dpll_mode - Working-modes a dpll can support. Modes
>>>>>>+differentiate
>>>>>>>how
>>>>>>+ * dpll selects one of its sources to syntonize with a source.
>>>>>>+ *
>>>>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a
>>>>>>+ request
>>>>>>to dpll
>>>>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto
>>>>>>+ selected by
>>>>>>dpll
>>>>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover
>>>>>>available
>>>>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled
>>>>>>+ Oscillator
>>>>>
>>>>>Why does the user care which oscilator is run internally. It's
>>>>>freerun, isn't it? If you want to expose oscilator type, you should
>>>>>do it elsewhere.
>>>>>
>>>>
>>>>In NCO user might change frequency of an output, in freerun cannot.
>>>
>>>How this could be done?
>>>
>>
>>I guess by some internal synchronizer frequency dividers. Same as other
>>output (different then input) frequencies are achievable there.
>
>I ment uAPI wise. Speak Netlink.
>

1. DPLL_MODE_NCO is returned with DPLL_A_MODE_SUPPORTED when HW supports it.
2. DPLL_MODE_NCO is requested by the user if user wants control output
frequency or output frequency offset of a dpll.

From the documentation of ZL80032:
* Numerically controlled oscillator (NCO) behavior allows system software to 
steer DPLL frequency or synthesizer frequency with resolution better than 0.005
ppt
* Similar to freerun mode, but with frequency control. The output clock is the
configured frequency with a frequency offset specified by the dpll_df_offset_x
register. This write-only register changes the output frequency offset of the
DPLL


Thank you,
Arkadiusz

>>
>>Thanks,
>>Arkadiusz
>>
>>>
>>>[...]
>>

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

* RE: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-03-07 12:23               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-07 12:23 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, February 7, 2023 3:15 PM
>
>Mon, Feb 06, 2023 at 03:00:09AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Tuesday, January 31, 2023 3:01 PM
>>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>>Cc: Vadim Fedorenko <vadfed@meta.com>; Jakub Kicinski
>>><kuba@kernel.org>; Jonathan Lemon <jonathan.lemon@gmail.com>; Paolo
>>>Abeni <pabeni@redhat.com>; netdev@vger.kernel.org;
>>>linux-arm-kernel@lists.infradead.org; linux- clk@vger.kernel.org;
>>>Olech, Milena <milena.olech@intel.com>; Michalik, Michal
>>><michal.michalik@intel.com>
>>>Subject: Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base
>>>functions
>>>
>>>Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com
>>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Thursday, January 19, 2023 6:16 PM
>>>>>
>>>>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>
>[...]
>
>
>>>>>>+			 struct dpll_pin_ops *ops, void *priv) {
>>>>>>+	struct dpll_pin *pin;
>>>>>>+	int ret;
>>>>>>+
>>>>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>>>>+					  shared_pin_description);
>>>>>>+	if (!pin) {
>>>>>>+		ret = -EINVAL;
>>>>>>+		goto unlock;
>>>>>>+	}
>>>>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>>>>+unlock:
>>>>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>>>>+
>>>>>>+	return ret;
>>>>>
>>>>>I don't understand why there should be a separate function to
>>>>>register the shared pin. As I see it, there is a pin object that
>>>>>could be registered with 2 or more dpll devices. What about having:
>>>>>
>>>>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>>>>dpll_pin_register(dpll_1, pin); dpll_pin_register(dpll_2, pin);
>>>>>dpll_pin_register(dpll_3, pin);
>>>>>
>>>>
>>>>IMHO your example works already, but it would possible only if the
>>>>same driver instance initializes all dplls.
>>>
>>>It should be only one instance of dpll to be shared between driver
>>>instances as I wrote in the reply to the "ice" part. There might he
>>>some pins created alongside with this.
>>>
>>
>>pin = dpll_pin_alloc(desc, type, ops, priv) dpll_pin_register(dpll_1,
>>pin); dpll_pin_register(dpll_2, pin); dpll_pin_register(dpll_3, pin); ^
>>there is registration of a single pin by a 3 dpll instances, and a
>>kernel module instance which registers them has a reference to the pin
>>and all dplls, thus it can just register them all without any problems,
>>don't need to call dpll_shared_pin_register(..).
>>
>>Now imagine 2 kernel module instances.
>>One (#1) creates one dpll, registers pins with it.
>>Second (#2) creates second dpll, and want to use/register pins of dpll
>>registered by the first instance (#1).
>
>Sure, both instances should be available to both module instances, using
>the suggested get/put create/reference system.
>Whichever module instance does register shared pin can use
>dpll_pin_register(), I see no problem with that.
>

In v6 those suggestions are implemented.
AFAIK Vadim shall send it soon.

>
>>
>>>My point is, the first driver instance which creates dpll registers
>>>also the pins. The other driver instance does not do anything, just
>>>gets reference to the dpll.
>>>
>>>On cleanup path, the last driver instance tearing down would
>>>unregister dpll pins (Could be done automatically by dpll_device_put()).
>>>
>>>There might be some other pins (Synce) created per driver instance
>>>(per-PF). You have to distinguish these 2 groups.
>>>
>>>
>>>>dpll_shared_pin_register is designed for driver instances without the
>>>>pin
>>>
>>>I think we need to make sure the terms are correct "sharing" is
>>>between multiple dpll instances. However, if 2 driver instances are
>>>sharing the same dpll instance, this instance has pins. There is no
>>>sharing unless there is another dpll instance in picture. Correct?
>>>
>>
>>Yes!
>>If two kernel module intances sharing a dpll instance, the pins belong
>>to the dpll instance, and yes each kernel module instance can register
>>pins with that dpll instance just with: dpll_pin_register(dpll_1, pin);
>>
>>dpll_shared_pin_register(..) shall be used when separated kernel module
>>instances are initializing separated dpll instances, and those
>>instances are
>
>Why exacly would they do that? Could you please draw me an example?
>

I think we shall not follow this discussion as in v6 we already
have the mechanics you suggested, but sure:
+----------+                 
|i0 - GPS  |--------------\
+----------+              |
+----------+              |
|i1 - SMA1 |------------\ |
+----------+            | |
+----------+            | |
|i2 - SMA2 |----------\ | |
+----------+          | | |
                      | | |
+---------------------|-|-|-------------------------------------------+
| Channel A / FW0     | | |     +-------------+   +---+   +--------+  |
|                     | | |-i0--|Synchronizer0|---|   |---| PHY0.0 |--|
|         +---+       | | |     |             |   |   |   +--------+  |
| PHY0.0--|   |       | |---i1--|             |---| M |---| PHY0.1 |--|
|         |   |       | | |     | +-----+     |   | A |   +--------+  |
| PHY0.1--| M |       |-----i2--| |DPLL0|     |   | C |---| PHY0.2 |--|
|         | U |       | | |     | +-----+     |   | 0 |   +--------+  |
| PHY0.2--| X |--+----------i3--| +-----+     |---|   |---| ...    |--|
|         | 0 |  |    | | |     | |DPLL1|     |   |   |   +--------+  |
| ...   --|   |  | /--------i4--| +-----+     |---|   |---| PHY0.7 |--|
|         |   |  | |  | | |     +-------------+   +---+   +--------+  |
| PHY0.7--|   |  | |  | | |                                           |
|         +---+  | |  | | |                                           |
+----------------|-|--|-|-|-------------------------------------------+
| Channel B / FW1| |  | | |     +-------------+   +---+   +--------+  |
|                | |  | | \-i0--|Synchronizer1|---|   |---| PHY1.0 |--|
|         +---+  | |  | |       |             |   |   |   +--------+  |
| PHY1.0--|   |  | |  | \---i1--|             |---| M |---| PHY1.1 |--|
|         |   |  | |  |         | +-----+     |   | A |   +--------+  |
| PHY1.1--| M |  | |  \-----i2--| |DPLL0|     |   | C |---| PHY1.2 |--|
|         | U |  | |            | +-----+     |   | 1 |   +--------+  |
| PHY1.2--| X |  \-|--------i3--| +-----+     |---|   |---| ...    |--|
|         | 1 |    |            | |DPLL1|     |   |   |   +--------+  |
| ...   --|   |----+--------i4--| +-----+     |---|   |---| PHY1.7 |--|
|         |   |                 +-------------+   +---+   +--------+  |
| PHY1.7--|   |                                                       |
|         +---+                                                       |
+---------------------------------------------------------------------+

>
>>physically sharing their pins.
>>
>>>
>
>[...]
>
>
>>>>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>>>>+				   const struct dpll_device *dpll,
>>>>>>+				   const struct dpll_pin *pin) {
>>>>>>+	enum dpll_pin_mode i;
>>>>>>+	bool active;
>>>>>>+
>>>>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>>>>+			return 0;
>>>>>>+		if (active)
>>>>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>>>>
>>>>>Why this is signed?
>>>>>
>>>>
>>>>Because enums are signed.
>>>
>>>You use negative values in enums? Don't do that here. Have all netlink
>>>atrributes unsigned please.
>>>
>>
>>No, we don't use negative values, but enum is a signed type by itself.
>>Doesn't seem right thing to do, put signed-type value into unsigned type TLV.
>>This smells very bad.
>
>Well, then all existing uses that carry enum over netlink attributes smell
>bad. The enum values are all unsigned, I see no reason to use S*.
>Please be consistent with the rest of the Netlink uAPI.
>

Yes, exactly, don't know why to follow bad practicies, saying "that's how it's
done". Is there any reasoning behind this?

>
>[...]
>
>>>>>>+
>>>>>>+/* dpll_pin_signal_type - signal types
>>>>>>+ *
>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>>>>
>>>>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we
>>>>>work with HZ value directly? For example, supported freq:
>>>>>1, 10000000
>>>>>or:
>>>>>1, 1000
>>>>>
>>>>>freq set 10000000
>>>>>freq set 1
>>>>>
>>>>>Simple and easy.
>>>>>
>>>>
>>>>AFAIR, we wanted to have most commonly used frequencies as enums +
>>>>custom_freq for some exotic ones (please note that there is also
>>>>possible 2PPS, which is
>>>>0.5 Hz).
>>>
>>>In this exotic case, user might add divider netlink attribute to
>>>divide the frequency pass in the attr. No problem.
>>>
>>>
>>>>This was design decision we already agreed on.
>>>>The userspace shall get definite list of comonly used frequencies
>>>>that can be used with given HW, it clearly enums are good for this.
>>>
>>>I don't see why. Each instance supports a set of frequencies. It would
>>>pass the values to the userspace.
>>>
>>>I fail to see the need to have some fixed values listed in enums.
>>>Mixing approaches for a single attribute is wrong. In ethtool we also
>>>don't have enum values for 10,100,1000mbits etc. It's just a number.
>>>
>>
>>In ethtool there are defines for linkspeeds.
>>There must be list of defines/enums to check the driver if it is supported.
>>Especially for ANY_FREQ we don't want to call driver 25 milions times or
>>more.
>
>Any is not really *any* is it? A simple range wouldn't do then? It would be
>much better to tell the user the boundaries.
>

In v6 those suggestions are implemented.

>
>>
>>Also, we have to move supported frequencies to the dpll_pin_alloc as it
>>is constant argument, supported frequencies shall not change @ runtime?
>>In such case there seems to be only one way to pass in a nice way, as a
>>bitmask?
>
>array of numbers (perhaps using defines for most common values), I don't
>see any problem in that. But you are talking about in-kernel API. Does not
>matter that much. What we are discussing is uAPI and that matters a lot.
>
>
>>
>>Back to the userspace part, do you suggest to have DPLLA_PIN_FREQ
>>attribute and translate kernelspace enum values to userspace defines
>>like DPLL_FREQ_1_HZ, etc? also with special define for supported ones
>>ANY_FREQ?
>
>Whichever is convenient. My focus here is uAPI.
>

In v6 those suggestions are implemented.

>
>>
>>>
>>>>
>>>>>
>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal,
>>>>>>+ value
>>>>>>defined
>>>>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>>>>+ **/
>>>>>>+enum dpll_pin_signal_type {
>>>>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>>>>+
>>>>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>>>>+};
>>>>>>+
>>>>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>>>>+
>>>>>>+/* dpll_pin_mode - available pin states
>>>>>>+ *
>>>>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin  **/ enum
>>>>>>+dpll_pin_mode {
>>>>>>+	DPLL_PIN_MODE_UNSPEC,
>>>>>>+	DPLL_PIN_MODE_CONNECTED,
>>>>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>>>>+	DPLL_PIN_MODE_SOURCE,
>>>>>>+	DPLL_PIN_MODE_OUTPUT,
>>>>>
>>>>>I don't follow. I see 2 enums:
>>>>>CONNECTED/DISCONNECTED
>>>>>SOURCE/OUTPUT
>>>>>why this is mangled together? How is it supposed to be working. Like
>>>>>a bitarray?
>>>>>
>>>>
>>>>The userspace shouldn't worry about bits, it recieves a list of
>>>attributes.
>>>>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>>>>DPLLA_PIN_MODE_SUPPORTED. I.e.
>>>>
>>>>	DPLLA_PIN_IDX			0
>>>>	DPLLA_PIN_MODE			1,3
>>>>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4
>>>
>>>I believe that mixing apples and oranges in a single attr is not correct.
>>>Could you please split to separate attrs as drafted below?
>>>
>>>>
>>>>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>>>>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>>>>and bitmask is not a way to go for userspace.
>>>
>>>What? See nla_bitmap.
>>>
>>
>>AFAIK, nla_bitmap is not yet merged.
>
>NLA_BITFIELD32
>
>
>>
>>>Anyway, why can't you have:
>>>DPLLA_PIN_CONNECTED     u8 1/0 (bool)
>>>DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }
>>
>>Don't get it, why this shall be u8 with bool value, doesn't make much
>>sense for userspace.
>
>Could be NLA_FLAG.
>
>
>>All the other attributes have enum type, we can go with separated
>>attribute:
>>DPLLA_PIN_STATE		enum { CONNECTED/DISCONNECTED }
>
>Yeah, why not. I think this is probably better and more explicit than
>NLA_FLAG.
>
>
>>Just be consistent and clear, and yes u8 is enough it to keep it, as
>>well as all of attribute enum values, so we can use u8 instead of u32 for
>>all of them.
>
>Yes, that is what is done normally for attrs like this.
>
>

In v6, there are enums and attributes:
DPLL_A_PIN_STATE	enum { CONNECTED/DISCONNECTED }
DPLL_A_PIN_DIRECTION	enum { SOURCE/OUTPUT }

also new capabilities attributes DPLL_A_PIN_DPLL_CAPS
a bitmap - implicit from u32 value.

>>
>>Actually for "connected/disconnected"-part there are 2 valid use-cases
>>on my
>>mind:
>>- pin can be connected with a number of "parents" (dplls or muxed-pins)
>>- pin is disconnected entirely
>>Second case can be achieved with control over first one, thus not need
>>for any special approach here. Proper control would be to let userspace
>>connect or disconnect a pin per each node it can be connected with, right?
>>
>>Then example dump of "get-pins" could look like this:
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		0
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_EXT
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>
>Nit, make sure you have this as 2 attrs, busname, devname.

Sure.

>
>
>>		DPLLA_PIN_STATE		CONNECTED
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		1
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>>
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		1
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		1
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		CONNECTED
>>
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		2
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		1
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>
>Okay.
>
>
>>
>>(similar for muxed pins)
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		3
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	DPLLA_PIN_PARENT		(nested)
>>		DPLLA_PIN_IDX		1
>>		DPLLA_PIN_STATE		DISCONNECTED
>>	DPLLA_PIN_PARENT		(nested)
>>		DPLLA_PIN_IDX		2
>>		DPLLA_PIN_STATE		CONNECTED
>>
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		4
>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>>	DPLLA_PIN_DIRECTION	SOURCE
>>	DPLLA_PIN_PARENT		(nested)
>>		DPLLA_PIN_IDX		1
>>		DPLLA_PIN_STATE		CONNECTED
>>	DPLLA_PIN_PARENT		(nested)
>>		DPLLA_PIN_IDX		2
>>		DPLLA_PIN_STATE		DISCONNECTED
>
>Looks fine.
>
>
>>
>>For DPLL_MODE_MANUAL a DPLLA_PIN_STATE would serve also as signal
>>selector mechanism.
>
>Yep, I already make this point in earlier rfc review comment.
>

Thanks for that :)

>
>>In above example DPLL_ID=0 has only "connected" DPLL_PIN_IDX=0, now
>>when different pin "connect" is requested:
>>
>>dpll-set request:
>>DPLLA_DPLL	(nested)
>>	DPLLA_ID=0
>>	DPLLA_NAME=pci_0000:00:00.0
>>DPLLA_PIN
>>	DPLLA_PIN_IDX=2
>>	DPLLA_PIN_CONNECTED=1
>>
>>Former shall "disconnect"..
>>And now, dump pin-get:
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		0
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		DISCONNECTED
>>...
>>DPLL_PIN	(nested)
>>	DPLLA_PIN_IDX		2
>>	...
>>	DPLLA_DPLL			(nested)
>>		DPLLA_ID		0
>>		DPLLA_NAME		pci_0000:00:00.0
>>		DPLLA_PIN_STATE		CONNECTED
>>
>>At least that shall happen on hardware level, right?
>>
>>As I can't find a use-case to have a pin "connected" but not "selected"
>>in case of DPLL_MODE_MANUAL.
>
>Exactly.
>
>
>>
>>A bit different is with DPLL_MODE_AUTOMATIC, the pins that connects
>>with dpll directly could be all connected, and their selection is
>>auto-controlled with a DPLLA_PIN_PRIO.
>>But still the user may also request to disconnect a pin - not use it at
>>all (instead of configuring lowest priority - which allows to use it,
>>if all other pins propagate invalid signal).
>>
>>Thus, for DPLL_MODE_AUTOMATIC all ablove is the same with a one
>>difference, each pin/dpll pair would have a prio, like suggested in the
>other email.
>>DPLLA_PIN	(nested)
>>	...
>>	DPLLA_DPLL	(nested)
>>		...
>>		DPLLA_PIN_CONNECTED	<connected value>
>>		DPLLA_PIN_STATE		<prio value>
>
>I think you made a mistake. Should it be:
>		DPLLA_PIN_STATE		<connected value>
>		DPLLA_PIN_PRIO		<prio value>
>?
>

Yes, exactly.

>
>>
>>Which basically means that both DPLL_A_PIN_PRIO and DPLLA_PIN_STATE
>>shall be a property of a PIN-DPLL pair, and configured as such.
>
>Yes.
>
>
>>
>>
>>>DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED,
>>>CAN_CHANGE_DIRECTION)
>>>
>>>We can use the capabilitis bitfield eventually for other purposes as
>>>well, it is going to be handy I'm sure.
>>>
>>
>>Well, in general I like the idea, altough the details...
>>We have 3 configuration levels:
>>- DPLL
>>- DPLL/PIN
>>- PIN
>>
>>Considering that, there is space for 3 of such CAPABILITIES attributes, but:
>>- DPLL can only configure MODE for now, so we can only convert
>>DPLL_A_MODE_SUPPORTED to a bitfield, and add DPLL_CAPS later if
>>required
>
>Can't do that. It's uAPI, once you have ATTR there, it's there for
>eternity...
>

I am not saying to remove something but add in the future.

>
>>- DPLL/PIN pair has configurable DPLLA_PIN_PRIO and DPLLA_PIN_STATE, so
>>we could introduce DPLLA_PIN_DPLL_CAPS for them
>
>Yeah.
>
>
>>- PIN has now configurable frequency (but this is done by providing
>>list of supported ones - no need for extra attribute). We already know
>>that pin shall also have optional features, like phase offset, embedded
>>sync.
>>For embedded sync if supported it shall also be a set of supported
>>frequencies.
>>Possibly for phase offset we could use similar CAPS field, but don't
>>think will manage this into next version.
>>
>>>
>>>
>>>>
>>>>
>>>>>
>>>>>>+
>>>>>>+	__DPLL_PIN_MODE_MAX,
>>>>>>+};
>>>>>>+
>>>
>>>[...]
>>>
>>>
>>>>>>+/**
>>>>>>+ * dpll_mode - Working-modes a dpll can support. Modes
>>>>>>+differentiate
>>>>>>>how
>>>>>>+ * dpll selects one of its sources to syntonize with a source.
>>>>>>+ *
>>>>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a
>>>>>>+ request
>>>>>>to dpll
>>>>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto
>>>>>>+ selected by
>>>>>>dpll
>>>>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover
>>>>>>available
>>>>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled
>>>>>>+ Oscillator
>>>>>
>>>>>Why does the user care which oscilator is run internally. It's
>>>>>freerun, isn't it? If you want to expose oscilator type, you should
>>>>>do it elsewhere.
>>>>>
>>>>
>>>>In NCO user might change frequency of an output, in freerun cannot.
>>>
>>>How this could be done?
>>>
>>
>>I guess by some internal synchronizer frequency dividers. Same as other
>>output (different then input) frequencies are achievable there.
>
>I ment uAPI wise. Speak Netlink.
>

1. DPLL_MODE_NCO is returned with DPLL_A_MODE_SUPPORTED when HW supports it.
2. DPLL_MODE_NCO is requested by the user if user wants control output
frequency or output frequency offset of a dpll.

From the documentation of ZL80032:
* Numerically controlled oscillator (NCO) behavior allows system software to 
steer DPLL frequency or synthesizer frequency with resolution better than 0.005
ppt
* Similar to freerun mode, but with frequency control. The output clock is the
configured frequency with a frequency offset specified by the dpll_df_offset_x
register. This write-only register changes the output frequency offset of the
DPLL


Thank you,
Arkadiusz

>>
>>Thanks,
>>Arkadiusz
>>
>>>
>>>[...]
>>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* RE: [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
  2023-01-31 13:00         ` Jiri Pirko
@ 2023-03-07 12:24           ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-07 12:24 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, January 31, 2023 2:01 PM
>
>Fri, Jan 27, 2023 at 07:13:20PM CET, arkadiusz.kubalewski@intel.com wrote:
>>
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, January 19, 2023 3:54 PM
>>>
>>>Tue, Jan 17, 2023 at 07:00:51PM CET, vadfed@meta.com wrote:
>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>[...]
>
>
>>>>+/**
>>>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>>>+ * @work: pointer to kthread_work structure
>>>>+ *
>>>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>>>+ */
>>>>+static void ice_dpll_periodic_work(struct kthread_work *work) {
>>>>+	struct ice_dplls *d = container_of(work, struct ice_dplls,
>>>>work.work);
>>>>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>>>>+	struct ice_dpll *de = &pf->dplls.eec;
>>>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>>>+	int ret = 0;
>>>>+
>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>
>>>Why do you need to check the flag there, this would should not be ever
>>>scheduled in case the flag was not set.
>>>
>>
>>It's here rather for stopping the worker, It shall no longer reschedule
>>and bail out.
>
>How that can happen?
>

You might be right, I will take a closer look on this before final submission.

>
>
>>
>>>
>>>>+		return;
>>>>+	mutex_lock(&d->lock);
>>>>+	ret = ice_dpll_update_state(&pf->hw, de);
>>>>+	if (!ret)
>>>>+		ret = ice_dpll_update_state(&pf->hw, dp);
>>>>+	if (ret) {
>>>>+		d->cgu_state_acq_err_num++;
>>>>+		/* stop rescheduling this worker */
>>>>+		if (d->cgu_state_acq_err_num >
>>>>+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
>>>>+			dev_err(ice_pf_to_dev(pf),
>>>>+				"EEC/PPS DPLLs periodic work disabled\n");
>>>>+			return;
>>>>+		}
>>>>+	}
>>>>+	mutex_unlock(&d->lock);
>>>>+	ice_dpll_notify_changes(de);
>>>>+	ice_dpll_notify_changes(dp);
>>>>+
>>>>+	/* Run twice a second or reschedule if update failed */
>>>>+	kthread_queue_delayed_work(d->kworker, &d->work,
>>>>+				   ret ? msecs_to_jiffies(10) :
>>>>+				   msecs_to_jiffies(500));
>>>>+}
>
>[...]
>
>
>>>>+/**
>>>>+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
>>>>+ * @pf: board private structure
>>>>+ *
>>>>+ * Return:
>>>>+ * * 0 - success
>>>>+ * * negative - init failure
>>>>+ */
>>>>+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf) {
>>>>+	u64 clock_id = 0;
>>>>+
>>>>+	ice_generate_clock_id(pf, &clock_id);
>>>>+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,
>>>
>>>I have to say I'm a bit lost in this code. Why exactly do you need
>>>this here? Looks like the pointer was set in ice_dpll_init_dpll().
>>>
>>>Or, is that in case of a different PF instantiating the DPLL instances?
>>
>>Yes it is, different PF is attaching recovered clock pins with this.
>>
>>>If yes, I'm pretty sure what it is wrong. What is the PF which did
>>>instanticate those unbinds? You have to share the dpll instance,
>>>refcount it.
>>>
>>
>>It will break, as in our case only one designated PF controls the dpll.
>
>You need to fix this then.
>

Yeah, with v6 we did the refcounts.

>
>>
>>>Btw, you have a problem during init as well, as the order matters.
>>>What if the other function probes only after executing this? You got
>>>-EFAULT here and bail out.
>>>
>>
>>We don't expect such use case, altough I see your point, will try to fix
>it.
>
>What? You have to be kidding me, correct? User obviously should have free
>will to use sysfs to bind/unbind the PCI devices in any order he pleases.
>
>
>>
>>>In mlx5, I also share one dpll instance between 2 PFs. What I do is I
>>>create mlx5-dpll instance which is refcounted, created by first probed
>>>PF and removed by the last one. In mlx5 case, the PFs are equal,
>>>nobody is an owner of the dpll. In your case, I think it is different.
>>>So probably better to implement the logic in driver then in the dpll
>core.
>>>
>>
>>For this NIC only one PF will control the dpll, so there is a designated
>owner.
>>Here owner must not only initialize a dpll but also register its pin,
>>so the other PFs could register additional pins. Basically it means for
>>ice that we can only rely on some postponed rclk initialization for a
>>case of unordered PF initialization. Seems doable.
>
>My point is, you should have one DPLL instance shared for muptiple PFs.
>Then, you have pin struct and dpll struct to use in pin_register and you
>can avoid this odd description magic which is based obviously on broken
>model you have.
>

v6 shall fix it.

>
>>
>>>Then you don't need dpll_device_get_by_clock_id at all. If you decide
>>>to implement that in dpll core, I believe that there should be some
>>>functions like:
>>>dpll = dpll_device_get(ops, clock_id, ...)  - to create/get reference
>>>dpll_device_put(dpll)                       - to put reference/destroy
>>
>>Sure, we can rename "dpll_device_get_by_clock_id" to "dpll_device_get"
>>(as it is only function currently exported for such behavior), and add
>>"dpll_device_put", with ref counts as suggested.
>>
>>>
>>>First caller of dpll_device_get() actually makes dpll to instantiate
>>>the device.
>>>
>>
>>Maybe I am missing something.. do you suggest that "dpll_device_get"
>>would allocate dpll device and do ref count, and then
>>dpll_device_register(..) call
>
>No need for separate register, is it? just have one dpll_device_get()
>function allocate-register/getref for you. Why do you need anything else?
>

v6 shall fix it.

Thank you,
Arkadiusz

>
>>would assign all the arguments that are available only in the owner
>>instance?
>>Or i got it wrong?
>
>[...]


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

* RE: [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu
@ 2023-03-07 12:24           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 44+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-03-07 12:24 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Tuesday, January 31, 2023 2:01 PM
>
>Fri, Jan 27, 2023 at 07:13:20PM CET, arkadiusz.kubalewski@intel.com wrote:
>>
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Thursday, January 19, 2023 3:54 PM
>>>
>>>Tue, Jan 17, 2023 at 07:00:51PM CET, vadfed@meta.com wrote:
>>>>From: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
>
>[...]
>
>
>>>>+/**
>>>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>>>+ * @work: pointer to kthread_work structure
>>>>+ *
>>>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>>>+ */
>>>>+static void ice_dpll_periodic_work(struct kthread_work *work) {
>>>>+	struct ice_dplls *d = container_of(work, struct ice_dplls,
>>>>work.work);
>>>>+	struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>>>>+	struct ice_dpll *de = &pf->dplls.eec;
>>>>+	struct ice_dpll *dp = &pf->dplls.pps;
>>>>+	int ret = 0;
>>>>+
>>>>+	if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>>
>>>Why do you need to check the flag there, this would should not be ever
>>>scheduled in case the flag was not set.
>>>
>>
>>It's here rather for stopping the worker, It shall no longer reschedule
>>and bail out.
>
>How that can happen?
>

You might be right, I will take a closer look on this before final submission.

>
>
>>
>>>
>>>>+		return;
>>>>+	mutex_lock(&d->lock);
>>>>+	ret = ice_dpll_update_state(&pf->hw, de);
>>>>+	if (!ret)
>>>>+		ret = ice_dpll_update_state(&pf->hw, dp);
>>>>+	if (ret) {
>>>>+		d->cgu_state_acq_err_num++;
>>>>+		/* stop rescheduling this worker */
>>>>+		if (d->cgu_state_acq_err_num >
>>>>+		    CGU_STATE_ACQ_ERR_THRESHOLD) {
>>>>+			dev_err(ice_pf_to_dev(pf),
>>>>+				"EEC/PPS DPLLs periodic work disabled\n");
>>>>+			return;
>>>>+		}
>>>>+	}
>>>>+	mutex_unlock(&d->lock);
>>>>+	ice_dpll_notify_changes(de);
>>>>+	ice_dpll_notify_changes(dp);
>>>>+
>>>>+	/* Run twice a second or reschedule if update failed */
>>>>+	kthread_queue_delayed_work(d->kworker, &d->work,
>>>>+				   ret ? msecs_to_jiffies(10) :
>>>>+				   msecs_to_jiffies(500));
>>>>+}
>
>[...]
>
>
>>>>+/**
>>>>+ * ice_dpll_rclk_find_dplls - find the device-wide DPLLs by clock_id
>>>>+ * @pf: board private structure
>>>>+ *
>>>>+ * Return:
>>>>+ * * 0 - success
>>>>+ * * negative - init failure
>>>>+ */
>>>>+static int ice_dpll_rclk_find_dplls(struct ice_pf *pf) {
>>>>+	u64 clock_id = 0;
>>>>+
>>>>+	ice_generate_clock_id(pf, &clock_id);
>>>>+	pf->dplls.eec.dpll = dpll_device_get_by_clock_id(clock_id,
>>>
>>>I have to say I'm a bit lost in this code. Why exactly do you need
>>>this here? Looks like the pointer was set in ice_dpll_init_dpll().
>>>
>>>Or, is that in case of a different PF instantiating the DPLL instances?
>>
>>Yes it is, different PF is attaching recovered clock pins with this.
>>
>>>If yes, I'm pretty sure what it is wrong. What is the PF which did
>>>instanticate those unbinds? You have to share the dpll instance,
>>>refcount it.
>>>
>>
>>It will break, as in our case only one designated PF controls the dpll.
>
>You need to fix this then.
>

Yeah, with v6 we did the refcounts.

>
>>
>>>Btw, you have a problem during init as well, as the order matters.
>>>What if the other function probes only after executing this? You got
>>>-EFAULT here and bail out.
>>>
>>
>>We don't expect such use case, altough I see your point, will try to fix
>it.
>
>What? You have to be kidding me, correct? User obviously should have free
>will to use sysfs to bind/unbind the PCI devices in any order he pleases.
>
>
>>
>>>In mlx5, I also share one dpll instance between 2 PFs. What I do is I
>>>create mlx5-dpll instance which is refcounted, created by first probed
>>>PF and removed by the last one. In mlx5 case, the PFs are equal,
>>>nobody is an owner of the dpll. In your case, I think it is different.
>>>So probably better to implement the logic in driver then in the dpll
>core.
>>>
>>
>>For this NIC only one PF will control the dpll, so there is a designated
>owner.
>>Here owner must not only initialize a dpll but also register its pin,
>>so the other PFs could register additional pins. Basically it means for
>>ice that we can only rely on some postponed rclk initialization for a
>>case of unordered PF initialization. Seems doable.
>
>My point is, you should have one DPLL instance shared for muptiple PFs.
>Then, you have pin struct and dpll struct to use in pin_register and you
>can avoid this odd description magic which is based obviously on broken
>model you have.
>

v6 shall fix it.

>
>>
>>>Then you don't need dpll_device_get_by_clock_id at all. If you decide
>>>to implement that in dpll core, I believe that there should be some
>>>functions like:
>>>dpll = dpll_device_get(ops, clock_id, ...)  - to create/get reference
>>>dpll_device_put(dpll)                       - to put reference/destroy
>>
>>Sure, we can rename "dpll_device_get_by_clock_id" to "dpll_device_get"
>>(as it is only function currently exported for such behavior), and add
>>"dpll_device_put", with ref counts as suggested.
>>
>>>
>>>First caller of dpll_device_get() actually makes dpll to instantiate
>>>the device.
>>>
>>
>>Maybe I am missing something.. do you suggest that "dpll_device_get"
>>would allocate dpll device and do ref count, and then
>>dpll_device_register(..) call
>
>No need for separate register, is it? just have one dpll_device_get()
>function allocate-register/getref for you. Why do you need anything else?
>

v6 shall fix it.

Thank you,
Arkadiusz

>
>>would assign all the arguments that are available only in the owner
>>instance?
>>Or i got it wrong?
>
>[...]


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
  2023-03-07 12:23               ` Kubalewski, Arkadiusz
@ 2023-03-07 14:10                 ` Jiri Pirko
  -1 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-03-07 14:10 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

Tue, Mar 07, 2023 at 01:23:27PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, February 7, 2023 3:15 PM
>>
>>Mon, Feb 06, 2023 at 03:00:09AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Tuesday, January 31, 2023 3:01 PM
>>>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>>>Cc: Vadim Fedorenko <vadfed@meta.com>; Jakub Kicinski
>>>><kuba@kernel.org>; Jonathan Lemon <jonathan.lemon@gmail.com>; Paolo
>>>>Abeni <pabeni@redhat.com>; netdev@vger.kernel.org;
>>>>linux-arm-kernel@lists.infradead.org; linux- clk@vger.kernel.org;
>>>>Olech, Milena <milena.olech@intel.com>; Michalik, Michal
>>>><michal.michalik@intel.com>
>>>>Subject: Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base
>>>>functions
>>>>
>>>>Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Thursday, January 19, 2023 6:16 PM
>>>>>>
>>>>>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>>
>>[...]
>>
>>
>>>>>>>+			 struct dpll_pin_ops *ops, void *priv) {
>>>>>>>+	struct dpll_pin *pin;
>>>>>>>+	int ret;
>>>>>>>+
>>>>>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>>>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>>>>>+					  shared_pin_description);
>>>>>>>+	if (!pin) {
>>>>>>>+		ret = -EINVAL;
>>>>>>>+		goto unlock;
>>>>>>>+	}
>>>>>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>>>>>+unlock:
>>>>>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>>>>>+
>>>>>>>+	return ret;
>>>>>>
>>>>>>I don't understand why there should be a separate function to
>>>>>>register the shared pin. As I see it, there is a pin object that
>>>>>>could be registered with 2 or more dpll devices. What about having:
>>>>>>
>>>>>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>>>>>dpll_pin_register(dpll_1, pin); dpll_pin_register(dpll_2, pin);
>>>>>>dpll_pin_register(dpll_3, pin);
>>>>>>
>>>>>
>>>>>IMHO your example works already, but it would possible only if the
>>>>>same driver instance initializes all dplls.
>>>>
>>>>It should be only one instance of dpll to be shared between driver
>>>>instances as I wrote in the reply to the "ice" part. There might he
>>>>some pins created alongside with this.
>>>>
>>>
>>>pin = dpll_pin_alloc(desc, type, ops, priv) dpll_pin_register(dpll_1,
>>>pin); dpll_pin_register(dpll_2, pin); dpll_pin_register(dpll_3, pin); ^
>>>there is registration of a single pin by a 3 dpll instances, and a
>>>kernel module instance which registers them has a reference to the pin
>>>and all dplls, thus it can just register them all without any problems,
>>>don't need to call dpll_shared_pin_register(..).
>>>
>>>Now imagine 2 kernel module instances.
>>>One (#1) creates one dpll, registers pins with it.
>>>Second (#2) creates second dpll, and want to use/register pins of dpll
>>>registered by the first instance (#1).
>>
>>Sure, both instances should be available to both module instances, using
>>the suggested get/put create/reference system.
>>Whichever module instance does register shared pin can use
>>dpll_pin_register(), I see no problem with that.
>>
>
>In v6 those suggestions are implemented.
>AFAIK Vadim shall send it soon.

Good.


>
>>
>>>
>>>>My point is, the first driver instance which creates dpll registers
>>>>also the pins. The other driver instance does not do anything, just
>>>>gets reference to the dpll.
>>>>
>>>>On cleanup path, the last driver instance tearing down would
>>>>unregister dpll pins (Could be done automatically by dpll_device_put()).
>>>>
>>>>There might be some other pins (Synce) created per driver instance
>>>>(per-PF). You have to distinguish these 2 groups.
>>>>
>>>>
>>>>>dpll_shared_pin_register is designed for driver instances without the
>>>>>pin
>>>>
>>>>I think we need to make sure the terms are correct "sharing" is
>>>>between multiple dpll instances. However, if 2 driver instances are
>>>>sharing the same dpll instance, this instance has pins. There is no
>>>>sharing unless there is another dpll instance in picture. Correct?
>>>>
>>>
>>>Yes!
>>>If two kernel module intances sharing a dpll instance, the pins belong
>>>to the dpll instance, and yes each kernel module instance can register
>>>pins with that dpll instance just with: dpll_pin_register(dpll_1, pin);
>>>
>>>dpll_shared_pin_register(..) shall be used when separated kernel module
>>>instances are initializing separated dpll instances, and those
>>>instances are
>>
>>Why exacly would they do that? Could you please draw me an example?
>>
>
>I think we shall not follow this discussion as in v6 we already
>have the mechanics you suggested, but sure:

Ok.


>+----------+                 
>|i0 - GPS  |--------------\
>+----------+              |
>+----------+              |
>|i1 - SMA1 |------------\ |
>+----------+            | |
>+----------+            | |
>|i2 - SMA2 |----------\ | |
>+----------+          | | |
>                      | | |
>+---------------------|-|-|-------------------------------------------+
>| Channel A / FW0     | | |     +-------------+   +---+   +--------+  |
>|                     | | |-i0--|Synchronizer0|---|   |---| PHY0.0 |--|
>|         +---+       | | |     |             |   |   |   +--------+  |
>| PHY0.0--|   |       | |---i1--|             |---| M |---| PHY0.1 |--|
>|         |   |       | | |     | +-----+     |   | A |   +--------+  |
>| PHY0.1--| M |       |-----i2--| |DPLL0|     |   | C |---| PHY0.2 |--|
>|         | U |       | | |     | +-----+     |   | 0 |   +--------+  |
>| PHY0.2--| X |--+----------i3--| +-----+     |---|   |---| ...    |--|
>|         | 0 |  |    | | |     | |DPLL1|     |   |   |   +--------+  |
>| ...   --|   |  | /--------i4--| +-----+     |---|   |---| PHY0.7 |--|
>|         |   |  | |  | | |     +-------------+   +---+   +--------+  |
>| PHY0.7--|   |  | |  | | |                                           |
>|         +---+  | |  | | |                                           |
>+----------------|-|--|-|-|-------------------------------------------+
>| Channel B / FW1| |  | | |     +-------------+   +---+   +--------+  |
>|                | |  | | \-i0--|Synchronizer1|---|   |---| PHY1.0 |--|
>|         +---+  | |  | |       |             |   |   |   +--------+  |
>| PHY1.0--|   |  | |  | \---i1--|             |---| M |---| PHY1.1 |--|
>|         |   |  | |  |         | +-----+     |   | A |   +--------+  |
>| PHY1.1--| M |  | |  \-----i2--| |DPLL0|     |   | C |---| PHY1.2 |--|
>|         | U |  | |            | +-----+     |   | 1 |   +--------+  |
>| PHY1.2--| X |  \-|--------i3--| +-----+     |---|   |---| ...    |--|
>|         | 1 |    |            | |DPLL1|     |   |   |   +--------+  |
>| ...   --|   |----+--------i4--| +-----+     |---|   |---| PHY1.7 |--|
>|         |   |                 +-------------+   +---+   +--------+  |
>| PHY1.7--|   |                                                       |
>|         +---+                                                       |
>+---------------------------------------------------------------------+
>
>>
>>>physically sharing their pins.
>>>
>>>>
>>
>>[...]
>>
>>
>>>>>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>>>>>+				   const struct dpll_device *dpll,
>>>>>>>+				   const struct dpll_pin *pin) {
>>>>>>>+	enum dpll_pin_mode i;
>>>>>>>+	bool active;
>>>>>>>+
>>>>>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>>>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>>>>>+			return 0;
>>>>>>>+		if (active)
>>>>>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>>>>>
>>>>>>Why this is signed?
>>>>>>
>>>>>
>>>>>Because enums are signed.
>>>>
>>>>You use negative values in enums? Don't do that here. Have all netlink
>>>>atrributes unsigned please.
>>>>
>>>
>>>No, we don't use negative values, but enum is a signed type by itself.
>>>Doesn't seem right thing to do, put signed-type value into unsigned type TLV.
>>>This smells very bad.
>>
>>Well, then all existing uses that carry enum over netlink attributes smell
>>bad. The enum values are all unsigned, I see no reason to use S*.
>>Please be consistent with the rest of the Netlink uAPI.
>>
>
>Yes, exactly, don't know why to follow bad practicies, saying "that's how it's
>done". Is there any reasoning behind this?

Where exactly do you pass negative value? If you don't use U*. Simple as
that :)


>
>>
>>[...]
>>
>>>>>>>+
>>>>>>>+/* dpll_pin_signal_type - signal types
>>>>>>>+ *
>>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>>>>>
>>>>>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we
>>>>>>work with HZ value directly? For example, supported freq:
>>>>>>1, 10000000
>>>>>>or:
>>>>>>1, 1000
>>>>>>
>>>>>>freq set 10000000
>>>>>>freq set 1
>>>>>>
>>>>>>Simple and easy.
>>>>>>
>>>>>
>>>>>AFAIR, we wanted to have most commonly used frequencies as enums +
>>>>>custom_freq for some exotic ones (please note that there is also
>>>>>possible 2PPS, which is
>>>>>0.5 Hz).
>>>>
>>>>In this exotic case, user might add divider netlink attribute to
>>>>divide the frequency pass in the attr. No problem.
>>>>
>>>>
>>>>>This was design decision we already agreed on.
>>>>>The userspace shall get definite list of comonly used frequencies
>>>>>that can be used with given HW, it clearly enums are good for this.
>>>>
>>>>I don't see why. Each instance supports a set of frequencies. It would
>>>>pass the values to the userspace.
>>>>
>>>>I fail to see the need to have some fixed values listed in enums.
>>>>Mixing approaches for a single attribute is wrong. In ethtool we also
>>>>don't have enum values for 10,100,1000mbits etc. It's just a number.
>>>>
>>>
>>>In ethtool there are defines for linkspeeds.
>>>There must be list of defines/enums to check the driver if it is supported.
>>>Especially for ANY_FREQ we don't want to call driver 25 milions times or
>>>more.
>>
>>Any is not really *any* is it? A simple range wouldn't do then? It would be
>>much better to tell the user the boundaries.
>>
>
>In v6 those suggestions are implemented.

Good.


>
>>
>>>
>>>Also, we have to move supported frequencies to the dpll_pin_alloc as it
>>>is constant argument, supported frequencies shall not change @ runtime?
>>>In such case there seems to be only one way to pass in a nice way, as a
>>>bitmask?
>>
>>array of numbers (perhaps using defines for most common values), I don't
>>see any problem in that. But you are talking about in-kernel API. Does not
>>matter that much. What we are discussing is uAPI and that matters a lot.
>>
>>
>>>
>>>Back to the userspace part, do you suggest to have DPLLA_PIN_FREQ
>>>attribute and translate kernelspace enum values to userspace defines
>>>like DPLL_FREQ_1_HZ, etc? also with special define for supported ones
>>>ANY_FREQ?
>>
>>Whichever is convenient. My focus here is uAPI.
>>
>
>In v6 those suggestions are implemented.

Ok.


>
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal,
>>>>>>>+ value
>>>>>>>defined
>>>>>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>>>>>+ **/
>>>>>>>+enum dpll_pin_signal_type {
>>>>>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>>>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>>>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>>>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>>>>>+
>>>>>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>>>>>+};
>>>>>>>+
>>>>>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>>>>>+
>>>>>>>+/* dpll_pin_mode - available pin states
>>>>>>>+ *
>>>>>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>>>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>>>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>>>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>>>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin  **/ enum
>>>>>>>+dpll_pin_mode {
>>>>>>>+	DPLL_PIN_MODE_UNSPEC,
>>>>>>>+	DPLL_PIN_MODE_CONNECTED,
>>>>>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>>>>>+	DPLL_PIN_MODE_SOURCE,
>>>>>>>+	DPLL_PIN_MODE_OUTPUT,
>>>>>>
>>>>>>I don't follow. I see 2 enums:
>>>>>>CONNECTED/DISCONNECTED
>>>>>>SOURCE/OUTPUT
>>>>>>why this is mangled together? How is it supposed to be working. Like
>>>>>>a bitarray?
>>>>>>
>>>>>
>>>>>The userspace shouldn't worry about bits, it recieves a list of
>>>>attributes.
>>>>>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>>>>>DPLLA_PIN_MODE_SUPPORTED. I.e.
>>>>>
>>>>>	DPLLA_PIN_IDX			0
>>>>>	DPLLA_PIN_MODE			1,3
>>>>>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4
>>>>
>>>>I believe that mixing apples and oranges in a single attr is not correct.
>>>>Could you please split to separate attrs as drafted below?
>>>>
>>>>>
>>>>>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>>>>>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>>>>>and bitmask is not a way to go for userspace.
>>>>
>>>>What? See nla_bitmap.
>>>>
>>>
>>>AFAIK, nla_bitmap is not yet merged.
>>
>>NLA_BITFIELD32
>>
>>
>>>
>>>>Anyway, why can't you have:
>>>>DPLLA_PIN_CONNECTED     u8 1/0 (bool)
>>>>DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }
>>>
>>>Don't get it, why this shall be u8 with bool value, doesn't make much
>>>sense for userspace.
>>
>>Could be NLA_FLAG.
>>
>>
>>>All the other attributes have enum type, we can go with separated
>>>attribute:
>>>DPLLA_PIN_STATE		enum { CONNECTED/DISCONNECTED }
>>
>>Yeah, why not. I think this is probably better and more explicit than
>>NLA_FLAG.
>>
>>
>>>Just be consistent and clear, and yes u8 is enough it to keep it, as
>>>well as all of attribute enum values, so we can use u8 instead of u32 for
>>>all of them.
>>
>>Yes, that is what is done normally for attrs like this.
>>
>>
>
>In v6, there are enums and attributes:
>DPLL_A_PIN_STATE	enum { CONNECTED/DISCONNECTED }
>DPLL_A_PIN_DIRECTION	enum { SOURCE/OUTPUT }
>
>also new capabilities attributes DPLL_A_PIN_DPLL_CAPS
>a bitmap - implicit from u32 value.


Looking forward to it.


>
>>>
>>>Actually for "connected/disconnected"-part there are 2 valid use-cases
>>>on my
>>>mind:
>>>- pin can be connected with a number of "parents" (dplls or muxed-pins)
>>>- pin is disconnected entirely
>>>Second case can be achieved with control over first one, thus not need
>>>for any special approach here. Proper control would be to let userspace
>>>connect or disconnect a pin per each node it can be connected with, right?
>>>
>>>Then example dump of "get-pins" could look like this:
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		0
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_EXT
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>
>>Nit, make sure you have this as 2 attrs, busname, devname.
>
>Sure.

Good.


>
>>
>>
>>>		DPLLA_PIN_STATE		CONNECTED
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		1
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		1
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		1
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		CONNECTED
>>>
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		2
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		1
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>
>>Okay.
>>
>>
>>>
>>>(similar for muxed pins)
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		3
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	DPLLA_PIN_PARENT		(nested)
>>>		DPLLA_PIN_IDX		1
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>	DPLLA_PIN_PARENT		(nested)
>>>		DPLLA_PIN_IDX		2
>>>		DPLLA_PIN_STATE		CONNECTED
>>>
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		4
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	DPLLA_PIN_PARENT		(nested)
>>>		DPLLA_PIN_IDX		1
>>>		DPLLA_PIN_STATE		CONNECTED
>>>	DPLLA_PIN_PARENT		(nested)
>>>		DPLLA_PIN_IDX		2
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>
>>Looks fine.
>>
>>
>>>
>>>For DPLL_MODE_MANUAL a DPLLA_PIN_STATE would serve also as signal
>>>selector mechanism.
>>
>>Yep, I already make this point in earlier rfc review comment.
>>
>
>Thanks for that :)
>
>>
>>>In above example DPLL_ID=0 has only "connected" DPLL_PIN_IDX=0, now
>>>when different pin "connect" is requested:
>>>
>>>dpll-set request:
>>>DPLLA_DPLL	(nested)
>>>	DPLLA_ID=0
>>>	DPLLA_NAME=pci_0000:00:00.0
>>>DPLLA_PIN
>>>	DPLLA_PIN_IDX=2
>>>	DPLLA_PIN_CONNECTED=1
>>>
>>>Former shall "disconnect"..
>>>And now, dump pin-get:
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		0
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>...
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		2
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		CONNECTED
>>>
>>>At least that shall happen on hardware level, right?
>>>
>>>As I can't find a use-case to have a pin "connected" but not "selected"
>>>in case of DPLL_MODE_MANUAL.
>>
>>Exactly.
>>
>>
>>>
>>>A bit different is with DPLL_MODE_AUTOMATIC, the pins that connects
>>>with dpll directly could be all connected, and their selection is
>>>auto-controlled with a DPLLA_PIN_PRIO.
>>>But still the user may also request to disconnect a pin - not use it at
>>>all (instead of configuring lowest priority - which allows to use it,
>>>if all other pins propagate invalid signal).
>>>
>>>Thus, for DPLL_MODE_AUTOMATIC all ablove is the same with a one
>>>difference, each pin/dpll pair would have a prio, like suggested in the
>>other email.
>>>DPLLA_PIN	(nested)
>>>	...
>>>	DPLLA_DPLL	(nested)
>>>		...
>>>		DPLLA_PIN_CONNECTED	<connected value>
>>>		DPLLA_PIN_STATE		<prio value>
>>
>>I think you made a mistake. Should it be:
>>		DPLLA_PIN_STATE		<connected value>
>>		DPLLA_PIN_PRIO		<prio value>
>>?
>>
>
>Yes, exactly.
>
>>
>>>
>>>Which basically means that both DPLL_A_PIN_PRIO and DPLLA_PIN_STATE
>>>shall be a property of a PIN-DPLL pair, and configured as such.
>>
>>Yes.
>>
>>
>>>
>>>
>>>>DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED,
>>>>CAN_CHANGE_DIRECTION)
>>>>
>>>>We can use the capabilitis bitfield eventually for other purposes as
>>>>well, it is going to be handy I'm sure.
>>>>
>>>
>>>Well, in general I like the idea, altough the details...
>>>We have 3 configuration levels:
>>>- DPLL
>>>- DPLL/PIN
>>>- PIN
>>>
>>>Considering that, there is space for 3 of such CAPABILITIES attributes, but:
>>>- DPLL can only configure MODE for now, so we can only convert
>>>DPLL_A_MODE_SUPPORTED to a bitfield, and add DPLL_CAPS later if
>>>required
>>
>>Can't do that. It's uAPI, once you have ATTR there, it's there for
>>eternity...
>>
>
>I am not saying to remove something but add in the future.
>
>>
>>>- DPLL/PIN pair has configurable DPLLA_PIN_PRIO and DPLLA_PIN_STATE, so
>>>we could introduce DPLLA_PIN_DPLL_CAPS for them
>>
>>Yeah.
>>
>>
>>>- PIN has now configurable frequency (but this is done by providing
>>>list of supported ones - no need for extra attribute). We already know
>>>that pin shall also have optional features, like phase offset, embedded
>>>sync.
>>>For embedded sync if supported it shall also be a set of supported
>>>frequencies.
>>>Possibly for phase offset we could use similar CAPS field, but don't
>>>think will manage this into next version.
>>>
>>>>
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>>+
>>>>>>>+	__DPLL_PIN_MODE_MAX,
>>>>>>>+};
>>>>>>>+
>>>>
>>>>[...]
>>>>
>>>>
>>>>>>>+/**
>>>>>>>+ * dpll_mode - Working-modes a dpll can support. Modes
>>>>>>>+differentiate
>>>>>>>>how
>>>>>>>+ * dpll selects one of its sources to syntonize with a source.
>>>>>>>+ *
>>>>>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>>>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a
>>>>>>>+ request
>>>>>>>to dpll
>>>>>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto
>>>>>>>+ selected by
>>>>>>>dpll
>>>>>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>>>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover
>>>>>>>available
>>>>>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled
>>>>>>>+ Oscillator
>>>>>>
>>>>>>Why does the user care which oscilator is run internally. It's
>>>>>>freerun, isn't it? If you want to expose oscilator type, you should
>>>>>>do it elsewhere.
>>>>>>
>>>>>
>>>>>In NCO user might change frequency of an output, in freerun cannot.
>>>>
>>>>How this could be done?
>>>>
>>>
>>>I guess by some internal synchronizer frequency dividers. Same as other
>>>output (different then input) frequencies are achievable there.
>>
>>I ment uAPI wise. Speak Netlink.
>>
>
>1. DPLL_MODE_NCO is returned with DPLL_A_MODE_SUPPORTED when HW supports it.
>2. DPLL_MODE_NCO is requested by the user if user wants control output
>frequency or output frequency offset of a dpll.
>
>From the documentation of ZL80032:
>* Numerically controlled oscillator (NCO) behavior allows system software to 
>steer DPLL frequency or synthesizer frequency with resolution better than 0.005
>ppt
>* Similar to freerun mode, but with frequency control. The output clock is the
>configured frequency with a frequency offset specified by the dpll_df_offset_x
>register. This write-only register changes the output frequency offset of the
>DPLL

Okay, document it properly then, best in the header exposing this enum
value.


Thanks!

>
>
>Thank you,
>Arkadiusz
>
>>>
>>>Thanks,
>>>Arkadiusz
>>>
>>>>
>>>>[...]
>>>

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

* Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions
@ 2023-03-07 14:10                 ` Jiri Pirko
  0 siblings, 0 replies; 44+ messages in thread
From: Jiri Pirko @ 2023-03-07 14:10 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk, Olech, Milena, Michalik,
	Michal

Tue, Mar 07, 2023 at 01:23:27PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Tuesday, February 7, 2023 3:15 PM
>>
>>Mon, Feb 06, 2023 at 03:00:09AM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Tuesday, January 31, 2023 3:01 PM
>>>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>>>Cc: Vadim Fedorenko <vadfed@meta.com>; Jakub Kicinski
>>>><kuba@kernel.org>; Jonathan Lemon <jonathan.lemon@gmail.com>; Paolo
>>>>Abeni <pabeni@redhat.com>; netdev@vger.kernel.org;
>>>>linux-arm-kernel@lists.infradead.org; linux- clk@vger.kernel.org;
>>>>Olech, Milena <milena.olech@intel.com>; Michalik, Michal
>>>><michal.michalik@intel.com>
>>>>Subject: Re: [RFC PATCH v5 1/4] dpll: Add DPLL framework base
>>>>functions
>>>>
>>>>Fri, Jan 27, 2023 at 07:12:41PM CET, arkadiusz.kubalewski@intel.com
>>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Thursday, January 19, 2023 6:16 PM
>>>>>>
>>>>>>Tue, Jan 17, 2023 at 07:00:48PM CET, vadfed@meta.com wrote:
>>
>>[...]
>>
>>
>>>>>>>+			 struct dpll_pin_ops *ops, void *priv) {
>>>>>>>+	struct dpll_pin *pin;
>>>>>>>+	int ret;
>>>>>>>+
>>>>>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>>>>>+	pin = dpll_pin_get_by_description(dpll_pin_owner,
>>>>>>>+					  shared_pin_description);
>>>>>>>+	if (!pin) {
>>>>>>>+		ret = -EINVAL;
>>>>>>>+		goto unlock;
>>>>>>>+	}
>>>>>>>+	ret = dpll_pin_register(dpll, pin, ops, priv);
>>>>>>>+unlock:
>>>>>>>+	mutex_unlock(&dpll_pin_owner->lock);
>>>>>>>+
>>>>>>>+	return ret;
>>>>>>
>>>>>>I don't understand why there should be a separate function to
>>>>>>register the shared pin. As I see it, there is a pin object that
>>>>>>could be registered with 2 or more dpll devices. What about having:
>>>>>>
>>>>>>pin = dpll_pin_alloc(desc, type, ops, priv)
>>>>>>dpll_pin_register(dpll_1, pin); dpll_pin_register(dpll_2, pin);
>>>>>>dpll_pin_register(dpll_3, pin);
>>>>>>
>>>>>
>>>>>IMHO your example works already, but it would possible only if the
>>>>>same driver instance initializes all dplls.
>>>>
>>>>It should be only one instance of dpll to be shared between driver
>>>>instances as I wrote in the reply to the "ice" part. There might he
>>>>some pins created alongside with this.
>>>>
>>>
>>>pin = dpll_pin_alloc(desc, type, ops, priv) dpll_pin_register(dpll_1,
>>>pin); dpll_pin_register(dpll_2, pin); dpll_pin_register(dpll_3, pin); ^
>>>there is registration of a single pin by a 3 dpll instances, and a
>>>kernel module instance which registers them has a reference to the pin
>>>and all dplls, thus it can just register them all without any problems,
>>>don't need to call dpll_shared_pin_register(..).
>>>
>>>Now imagine 2 kernel module instances.
>>>One (#1) creates one dpll, registers pins with it.
>>>Second (#2) creates second dpll, and want to use/register pins of dpll
>>>registered by the first instance (#1).
>>
>>Sure, both instances should be available to both module instances, using
>>the suggested get/put create/reference system.
>>Whichever module instance does register shared pin can use
>>dpll_pin_register(), I see no problem with that.
>>
>
>In v6 those suggestions are implemented.
>AFAIK Vadim shall send it soon.

Good.


>
>>
>>>
>>>>My point is, the first driver instance which creates dpll registers
>>>>also the pins. The other driver instance does not do anything, just
>>>>gets reference to the dpll.
>>>>
>>>>On cleanup path, the last driver instance tearing down would
>>>>unregister dpll pins (Could be done automatically by dpll_device_put()).
>>>>
>>>>There might be some other pins (Synce) created per driver instance
>>>>(per-PF). You have to distinguish these 2 groups.
>>>>
>>>>
>>>>>dpll_shared_pin_register is designed for driver instances without the
>>>>>pin
>>>>
>>>>I think we need to make sure the terms are correct "sharing" is
>>>>between multiple dpll instances. However, if 2 driver instances are
>>>>sharing the same dpll instance, this instance has pins. There is no
>>>>sharing unless there is another dpll instance in picture. Correct?
>>>>
>>>
>>>Yes!
>>>If two kernel module intances sharing a dpll instance, the pins belong
>>>to the dpll instance, and yes each kernel module instance can register
>>>pins with that dpll instance just with: dpll_pin_register(dpll_1, pin);
>>>
>>>dpll_shared_pin_register(..) shall be used when separated kernel module
>>>instances are initializing separated dpll instances, and those
>>>instances are
>>
>>Why exacly would they do that? Could you please draw me an example?
>>
>
>I think we shall not follow this discussion as in v6 we already
>have the mechanics you suggested, but sure:

Ok.


>+----------+                 
>|i0 - GPS  |--------------\
>+----------+              |
>+----------+              |
>|i1 - SMA1 |------------\ |
>+----------+            | |
>+----------+            | |
>|i2 - SMA2 |----------\ | |
>+----------+          | | |
>                      | | |
>+---------------------|-|-|-------------------------------------------+
>| Channel A / FW0     | | |     +-------------+   +---+   +--------+  |
>|                     | | |-i0--|Synchronizer0|---|   |---| PHY0.0 |--|
>|         +---+       | | |     |             |   |   |   +--------+  |
>| PHY0.0--|   |       | |---i1--|             |---| M |---| PHY0.1 |--|
>|         |   |       | | |     | +-----+     |   | A |   +--------+  |
>| PHY0.1--| M |       |-----i2--| |DPLL0|     |   | C |---| PHY0.2 |--|
>|         | U |       | | |     | +-----+     |   | 0 |   +--------+  |
>| PHY0.2--| X |--+----------i3--| +-----+     |---|   |---| ...    |--|
>|         | 0 |  |    | | |     | |DPLL1|     |   |   |   +--------+  |
>| ...   --|   |  | /--------i4--| +-----+     |---|   |---| PHY0.7 |--|
>|         |   |  | |  | | |     +-------------+   +---+   +--------+  |
>| PHY0.7--|   |  | |  | | |                                           |
>|         +---+  | |  | | |                                           |
>+----------------|-|--|-|-|-------------------------------------------+
>| Channel B / FW1| |  | | |     +-------------+   +---+   +--------+  |
>|                | |  | | \-i0--|Synchronizer1|---|   |---| PHY1.0 |--|
>|         +---+  | |  | |       |             |   |   |   +--------+  |
>| PHY1.0--|   |  | |  | \---i1--|             |---| M |---| PHY1.1 |--|
>|         |   |  | |  |         | +-----+     |   | A |   +--------+  |
>| PHY1.1--| M |  | |  \-----i2--| |DPLL0|     |   | C |---| PHY1.2 |--|
>|         | U |  | |            | +-----+     |   | 1 |   +--------+  |
>| PHY1.2--| X |  \-|--------i3--| +-----+     |---|   |---| ...    |--|
>|         | 1 |    |            | |DPLL1|     |   |   |   +--------+  |
>| ...   --|   |----+--------i4--| +-----+     |---|   |---| PHY1.7 |--|
>|         |   |                 +-------------+   +---+   +--------+  |
>| PHY1.7--|   |                                                       |
>|         +---+                                                       |
>+---------------------------------------------------------------------+
>
>>
>>>physically sharing their pins.
>>>
>>>>
>>
>>[...]
>>
>>
>>>>>>>+static int dpll_msg_add_pin_modes(struct sk_buff *msg,
>>>>>>>+				   const struct dpll_device *dpll,
>>>>>>>+				   const struct dpll_pin *pin) {
>>>>>>>+	enum dpll_pin_mode i;
>>>>>>>+	bool active;
>>>>>>>+
>>>>>>>+	for (i = DPLL_PIN_MODE_UNSPEC + 1; i <= DPLL_PIN_MODE_MAX; i++) {
>>>>>>>+		if (dpll_pin_mode_active(dpll, pin, i, &active))
>>>>>>>+			return 0;
>>>>>>>+		if (active)
>>>>>>>+			if (nla_put_s32(msg, DPLLA_PIN_MODE, i))
>>>>>>
>>>>>>Why this is signed?
>>>>>>
>>>>>
>>>>>Because enums are signed.
>>>>
>>>>You use negative values in enums? Don't do that here. Have all netlink
>>>>atrributes unsigned please.
>>>>
>>>
>>>No, we don't use negative values, but enum is a signed type by itself.
>>>Doesn't seem right thing to do, put signed-type value into unsigned type TLV.
>>>This smells very bad.
>>
>>Well, then all existing uses that carry enum over netlink attributes smell
>>bad. The enum values are all unsigned, I see no reason to use S*.
>>Please be consistent with the rest of the Netlink uAPI.
>>
>
>Yes, exactly, don't know why to follow bad practicies, saying "that's how it's
>done". Is there any reasoning behind this?

Where exactly do you pass negative value? If you don't use U*. Simple as
that :)


>
>>
>>[...]
>>
>>>>>>>+
>>>>>>>+/* dpll_pin_signal_type - signal types
>>>>>>>+ *
>>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_UNSPEC - unspecified value
>>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_1_PPS - a 1Hz signal
>>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_10_MHZ - a 10 MHz signal
>>>>>>
>>>>>>Why we need to have 1HZ and 10MHZ hardcoded as enums? Why can't we
>>>>>>work with HZ value directly? For example, supported freq:
>>>>>>1, 10000000
>>>>>>or:
>>>>>>1, 1000
>>>>>>
>>>>>>freq set 10000000
>>>>>>freq set 1
>>>>>>
>>>>>>Simple and easy.
>>>>>>
>>>>>
>>>>>AFAIR, we wanted to have most commonly used frequencies as enums +
>>>>>custom_freq for some exotic ones (please note that there is also
>>>>>possible 2PPS, which is
>>>>>0.5 Hz).
>>>>
>>>>In this exotic case, user might add divider netlink attribute to
>>>>divide the frequency pass in the attr. No problem.
>>>>
>>>>
>>>>>This was design decision we already agreed on.
>>>>>The userspace shall get definite list of comonly used frequencies
>>>>>that can be used with given HW, it clearly enums are good for this.
>>>>
>>>>I don't see why. Each instance supports a set of frequencies. It would
>>>>pass the values to the userspace.
>>>>
>>>>I fail to see the need to have some fixed values listed in enums.
>>>>Mixing approaches for a single attribute is wrong. In ethtool we also
>>>>don't have enum values for 10,100,1000mbits etc. It's just a number.
>>>>
>>>
>>>In ethtool there are defines for linkspeeds.
>>>There must be list of defines/enums to check the driver if it is supported.
>>>Especially for ANY_FREQ we don't want to call driver 25 milions times or
>>>more.
>>
>>Any is not really *any* is it? A simple range wouldn't do then? It would be
>>much better to tell the user the boundaries.
>>
>
>In v6 those suggestions are implemented.

Good.


>
>>
>>>
>>>Also, we have to move supported frequencies to the dpll_pin_alloc as it
>>>is constant argument, supported frequencies shall not change @ runtime?
>>>In such case there seems to be only one way to pass in a nice way, as a
>>>bitmask?
>>
>>array of numbers (perhaps using defines for most common values), I don't
>>see any problem in that. But you are talking about in-kernel API. Does not
>>matter that much. What we are discussing is uAPI and that matters a lot.
>>
>>
>>>
>>>Back to the userspace part, do you suggest to have DPLLA_PIN_FREQ
>>>attribute and translate kernelspace enum values to userspace defines
>>>like DPLL_FREQ_1_HZ, etc? also with special define for supported ones
>>>ANY_FREQ?
>>
>>Whichever is convenient. My focus here is uAPI.
>>
>
>In v6 those suggestions are implemented.

Ok.


>
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>>+ * @DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ - custom frequency signal,
>>>>>>>+ value
>>>>>>>defined
>>>>>>>+ *	with pin's DPLLA_PIN_SIGNAL_TYPE_CUSTOM_FREQ attribute
>>>>>>>+ **/
>>>>>>>+enum dpll_pin_signal_type {
>>>>>>>+	DPLL_PIN_SIGNAL_TYPE_UNSPEC,
>>>>>>>+	DPLL_PIN_SIGNAL_TYPE_1_PPS,
>>>>>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>>>>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>>>>>>+
>>>>>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>>>>>+};
>>>>>>>+
>>>>>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>>>>>+
>>>>>>>+/* dpll_pin_mode - available pin states
>>>>>>>+ *
>>>>>>>+ * @DPLL_PIN_MODE_UNSPEC - unspecified value
>>>>>>>+ * @DPLL_PIN_MODE_CONNECTED - pin connected
>>>>>>>+ * @DPLL_PIN_MODE_DISCONNECTED - pin disconnected
>>>>>>>+ * @DPLL_PIN_MODE_SOURCE - pin used as an input pin
>>>>>>>+ * @DPLL_PIN_MODE_OUTPUT - pin used as an output pin  **/ enum
>>>>>>>+dpll_pin_mode {
>>>>>>>+	DPLL_PIN_MODE_UNSPEC,
>>>>>>>+	DPLL_PIN_MODE_CONNECTED,
>>>>>>>+	DPLL_PIN_MODE_DISCONNECTED,
>>>>>>>+	DPLL_PIN_MODE_SOURCE,
>>>>>>>+	DPLL_PIN_MODE_OUTPUT,
>>>>>>
>>>>>>I don't follow. I see 2 enums:
>>>>>>CONNECTED/DISCONNECTED
>>>>>>SOURCE/OUTPUT
>>>>>>why this is mangled together? How is it supposed to be working. Like
>>>>>>a bitarray?
>>>>>>
>>>>>
>>>>>The userspace shouldn't worry about bits, it recieves a list of
>>>>attributes.
>>>>>For current/active mode: DPLLA_PIN_MODE, and for supported modes:
>>>>>DPLLA_PIN_MODE_SUPPORTED. I.e.
>>>>>
>>>>>	DPLLA_PIN_IDX			0
>>>>>	DPLLA_PIN_MODE			1,3
>>>>>	DPLLA_PIN_MODE_SUPPORTED	1,2,3,4
>>>>
>>>>I believe that mixing apples and oranges in a single attr is not correct.
>>>>Could you please split to separate attrs as drafted below?
>>>>
>>>>>
>>>>>The reason for existance of both DPLL_PIN_MODE_CONNECTED and
>>>>>DPLL_PIN_MODE_DISCONNECTED, is that the user must request it somehow,
>>>>>and bitmask is not a way to go for userspace.
>>>>
>>>>What? See nla_bitmap.
>>>>
>>>
>>>AFAIK, nla_bitmap is not yet merged.
>>
>>NLA_BITFIELD32
>>
>>
>>>
>>>>Anyway, why can't you have:
>>>>DPLLA_PIN_CONNECTED     u8 1/0 (bool)
>>>>DPLLA_PIN_DIRECTION     enum { SOURCE/OUTPUT }
>>>
>>>Don't get it, why this shall be u8 with bool value, doesn't make much
>>>sense for userspace.
>>
>>Could be NLA_FLAG.
>>
>>
>>>All the other attributes have enum type, we can go with separated
>>>attribute:
>>>DPLLA_PIN_STATE		enum { CONNECTED/DISCONNECTED }
>>
>>Yeah, why not. I think this is probably better and more explicit than
>>NLA_FLAG.
>>
>>
>>>Just be consistent and clear, and yes u8 is enough it to keep it, as
>>>well as all of attribute enum values, so we can use u8 instead of u32 for
>>>all of them.
>>
>>Yes, that is what is done normally for attrs like this.
>>
>>
>
>In v6, there are enums and attributes:
>DPLL_A_PIN_STATE	enum { CONNECTED/DISCONNECTED }
>DPLL_A_PIN_DIRECTION	enum { SOURCE/OUTPUT }
>
>also new capabilities attributes DPLL_A_PIN_DPLL_CAPS
>a bitmap - implicit from u32 value.


Looking forward to it.


>
>>>
>>>Actually for "connected/disconnected"-part there are 2 valid use-cases
>>>on my
>>>mind:
>>>- pin can be connected with a number of "parents" (dplls or muxed-pins)
>>>- pin is disconnected entirely
>>>Second case can be achieved with control over first one, thus not need
>>>for any special approach here. Proper control would be to let userspace
>>>connect or disconnect a pin per each node it can be connected with, right?
>>>
>>>Then example dump of "get-pins" could look like this:
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		0
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_EXT
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>
>>Nit, make sure you have this as 2 attrs, busname, devname.
>
>Sure.

Good.


>
>>
>>
>>>		DPLLA_PIN_STATE		CONNECTED
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		1
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		1
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		1
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		CONNECTED
>>>
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		2
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_MUX
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		1
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>
>>Okay.
>>
>>
>>>
>>>(similar for muxed pins)
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		3
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	DPLLA_PIN_PARENT		(nested)
>>>		DPLLA_PIN_IDX		1
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>	DPLLA_PIN_PARENT		(nested)
>>>		DPLLA_PIN_IDX		2
>>>		DPLLA_PIN_STATE		CONNECTED
>>>
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		4
>>>	DPLLA_PIN_TYPE		DPLL_PIN_TYPE_SYNCE_ETH_PORT
>>>	DPLLA_PIN_DIRECTION	SOURCE
>>>	DPLLA_PIN_PARENT		(nested)
>>>		DPLLA_PIN_IDX		1
>>>		DPLLA_PIN_STATE		CONNECTED
>>>	DPLLA_PIN_PARENT		(nested)
>>>		DPLLA_PIN_IDX		2
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>
>>Looks fine.
>>
>>
>>>
>>>For DPLL_MODE_MANUAL a DPLLA_PIN_STATE would serve also as signal
>>>selector mechanism.
>>
>>Yep, I already make this point in earlier rfc review comment.
>>
>
>Thanks for that :)
>
>>
>>>In above example DPLL_ID=0 has only "connected" DPLL_PIN_IDX=0, now
>>>when different pin "connect" is requested:
>>>
>>>dpll-set request:
>>>DPLLA_DPLL	(nested)
>>>	DPLLA_ID=0
>>>	DPLLA_NAME=pci_0000:00:00.0
>>>DPLLA_PIN
>>>	DPLLA_PIN_IDX=2
>>>	DPLLA_PIN_CONNECTED=1
>>>
>>>Former shall "disconnect"..
>>>And now, dump pin-get:
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		0
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		DISCONNECTED
>>>...
>>>DPLL_PIN	(nested)
>>>	DPLLA_PIN_IDX		2
>>>	...
>>>	DPLLA_DPLL			(nested)
>>>		DPLLA_ID		0
>>>		DPLLA_NAME		pci_0000:00:00.0
>>>		DPLLA_PIN_STATE		CONNECTED
>>>
>>>At least that shall happen on hardware level, right?
>>>
>>>As I can't find a use-case to have a pin "connected" but not "selected"
>>>in case of DPLL_MODE_MANUAL.
>>
>>Exactly.
>>
>>
>>>
>>>A bit different is with DPLL_MODE_AUTOMATIC, the pins that connects
>>>with dpll directly could be all connected, and their selection is
>>>auto-controlled with a DPLLA_PIN_PRIO.
>>>But still the user may also request to disconnect a pin - not use it at
>>>all (instead of configuring lowest priority - which allows to use it,
>>>if all other pins propagate invalid signal).
>>>
>>>Thus, for DPLL_MODE_AUTOMATIC all ablove is the same with a one
>>>difference, each pin/dpll pair would have a prio, like suggested in the
>>other email.
>>>DPLLA_PIN	(nested)
>>>	...
>>>	DPLLA_DPLL	(nested)
>>>		...
>>>		DPLLA_PIN_CONNECTED	<connected value>
>>>		DPLLA_PIN_STATE		<prio value>
>>
>>I think you made a mistake. Should it be:
>>		DPLLA_PIN_STATE		<connected value>
>>		DPLLA_PIN_PRIO		<prio value>
>>?
>>
>
>Yes, exactly.
>
>>
>>>
>>>Which basically means that both DPLL_A_PIN_PRIO and DPLLA_PIN_STATE
>>>shall be a property of a PIN-DPLL pair, and configured as such.
>>
>>Yes.
>>
>>
>>>
>>>
>>>>DPLLA_PIN_CAPS          nla_bitfield(CAN_CHANGE_CONNECTED,
>>>>CAN_CHANGE_DIRECTION)
>>>>
>>>>We can use the capabilitis bitfield eventually for other purposes as
>>>>well, it is going to be handy I'm sure.
>>>>
>>>
>>>Well, in general I like the idea, altough the details...
>>>We have 3 configuration levels:
>>>- DPLL
>>>- DPLL/PIN
>>>- PIN
>>>
>>>Considering that, there is space for 3 of such CAPABILITIES attributes, but:
>>>- DPLL can only configure MODE for now, so we can only convert
>>>DPLL_A_MODE_SUPPORTED to a bitfield, and add DPLL_CAPS later if
>>>required
>>
>>Can't do that. It's uAPI, once you have ATTR there, it's there for
>>eternity...
>>
>
>I am not saying to remove something but add in the future.
>
>>
>>>- DPLL/PIN pair has configurable DPLLA_PIN_PRIO and DPLLA_PIN_STATE, so
>>>we could introduce DPLLA_PIN_DPLL_CAPS for them
>>
>>Yeah.
>>
>>
>>>- PIN has now configurable frequency (but this is done by providing
>>>list of supported ones - no need for extra attribute). We already know
>>>that pin shall also have optional features, like phase offset, embedded
>>>sync.
>>>For embedded sync if supported it shall also be a set of supported
>>>frequencies.
>>>Possibly for phase offset we could use similar CAPS field, but don't
>>>think will manage this into next version.
>>>
>>>>
>>>>
>>>>>
>>>>>
>>>>>>
>>>>>>>+
>>>>>>>+	__DPLL_PIN_MODE_MAX,
>>>>>>>+};
>>>>>>>+
>>>>
>>>>[...]
>>>>
>>>>
>>>>>>>+/**
>>>>>>>+ * dpll_mode - Working-modes a dpll can support. Modes
>>>>>>>+differentiate
>>>>>>>>how
>>>>>>>+ * dpll selects one of its sources to syntonize with a source.
>>>>>>>+ *
>>>>>>>+ * @DPLL_MODE_UNSPEC - invalid
>>>>>>>+ * @DPLL_MODE_MANUAL - source can be only selected by sending a
>>>>>>>+ request
>>>>>>>to dpll
>>>>>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto
>>>>>>>+ selected by
>>>>>>>dpll
>>>>>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>>>>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover
>>>>>>>available
>>>>>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled
>>>>>>>+ Oscillator
>>>>>>
>>>>>>Why does the user care which oscilator is run internally. It's
>>>>>>freerun, isn't it? If you want to expose oscilator type, you should
>>>>>>do it elsewhere.
>>>>>>
>>>>>
>>>>>In NCO user might change frequency of an output, in freerun cannot.
>>>>
>>>>How this could be done?
>>>>
>>>
>>>I guess by some internal synchronizer frequency dividers. Same as other
>>>output (different then input) frequencies are achievable there.
>>
>>I ment uAPI wise. Speak Netlink.
>>
>
>1. DPLL_MODE_NCO is returned with DPLL_A_MODE_SUPPORTED when HW supports it.
>2. DPLL_MODE_NCO is requested by the user if user wants control output
>frequency or output frequency offset of a dpll.
>
>From the documentation of ZL80032:
>* Numerically controlled oscillator (NCO) behavior allows system software to 
>steer DPLL frequency or synthesizer frequency with resolution better than 0.005
>ppt
>* Similar to freerun mode, but with frequency control. The output clock is the
>configured frequency with a frequency offset specified by the dpll_df_offset_x
>register. This write-only register changes the output frequency offset of the
>DPLL

Okay, document it properly then, best in the header exposing this enum
value.


Thanks!

>
>
>Thank you,
>Arkadiusz
>
>>>
>>>Thanks,
>>>Arkadiusz
>>>
>>>>
>>>>[...]
>>>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

end of thread, other threads:[~2023-03-07 15:02 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-17 18:00 [RFC PATCH v5 0/4] Create common DPLL/clock configuration API Vadim Fedorenko
2023-01-17 18:00 ` Vadim Fedorenko
2023-01-17 18:00 ` [RFC PATCH v5 1/4] dpll: Add DPLL framework base functions Vadim Fedorenko
2023-01-17 18:00   ` Vadim Fedorenko
2023-01-19 17:16   ` Jiri Pirko
2023-01-19 17:16     ` Jiri Pirko
2023-01-20 12:56     ` Jiri Pirko
2023-01-20 12:56       ` Jiri Pirko
2023-01-27 18:12       ` Kubalewski, Arkadiusz
2023-01-27 18:12         ` Kubalewski, Arkadiusz
2023-01-27 18:12     ` Kubalewski, Arkadiusz
2023-01-27 18:12       ` Kubalewski, Arkadiusz
2023-01-31 14:00       ` Jiri Pirko
2023-01-31 14:00         ` Jiri Pirko
2023-02-06  2:00         ` Kubalewski, Arkadiusz
2023-02-06  2:00           ` Kubalewski, Arkadiusz
2023-02-07 14:15           ` Jiri Pirko
2023-02-07 14:15             ` Jiri Pirko
2023-03-07 12:23             ` Kubalewski, Arkadiusz
2023-03-07 12:23               ` Kubalewski, Arkadiusz
2023-03-07 14:10               ` Jiri Pirko
2023-03-07 14:10                 ` Jiri Pirko
2023-01-17 18:00 ` [RFC PATCH v5 2/4] dpll: documentation on DPLL subsystem interface Vadim Fedorenko
2023-01-17 18:00   ` Vadim Fedorenko
2023-01-17 18:00 ` [RFC PATCH v5 3/4] ice: add admin commands to access cgu configuration Vadim Fedorenko
2023-01-17 18:00   ` Vadim Fedorenko
2023-01-17 18:00 ` [RFC PATCH v5 4/4] ice: implement dpll interface to control cgu Vadim Fedorenko
2023-01-17 18:00   ` Vadim Fedorenko
2023-01-19 14:54   ` Jiri Pirko
2023-01-19 14:54     ` Jiri Pirko
2023-01-27 18:13     ` Kubalewski, Arkadiusz
2023-01-27 18:13       ` Kubalewski, Arkadiusz
2023-01-31 13:00       ` Jiri Pirko
2023-01-31 13:00         ` Jiri Pirko
2023-03-07 12:24         ` Kubalewski, Arkadiusz
2023-03-07 12:24           ` Kubalewski, Arkadiusz
2023-01-18 18:07 ` [RFC PATCH v5 0/4] Create common DPLL/clock configuration API Kubalewski, Arkadiusz
2023-01-18 18:07   ` Kubalewski, Arkadiusz
2023-01-19  0:15   ` Jakub Kicinski
2023-01-19  0:15     ` Jakub Kicinski
2023-01-19 11:48     ` Jiri Pirko
2023-01-19 11:48       ` Jiri Pirko
2023-01-19 17:23     ` Kubalewski, Arkadiusz
2023-01-19 17:23       ` Kubalewski, Arkadiusz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.