netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-11-29 21:37 Vadim Fedorenko
  2022-11-29 21:37 ` [RFC PATCH v4 1/4] dpll: add dpll_attr/dpll_pin_attr helper classes Vadim Fedorenko
                   ` (5 more replies)
  0 siblings, 6 replies; 87+ messages in thread
From: Vadim Fedorenko @ 2022-11-29 21:37 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, Vadim Fedorenko, 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.

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 (1):
  dpll: add dpll_attr/dpll_pin_attr helper classes

Vadim Fedorenko (3):
  dpll: Add DPLL framework base functions
  dpll: documentation on DPLL subsystem interface
  ptp_ocp: implement DPLL ops

 Documentation/networking/dpll.rst  | 271 ++++++++
 Documentation/networking/index.rst |   1 +
 MAINTAINERS                        |   8 +
 drivers/Kconfig                    |   2 +
 drivers/Makefile                   |   1 +
 drivers/dpll/Kconfig               |   7 +
 drivers/dpll/Makefile              |  11 +
 drivers/dpll/dpll_attr.c           | 278 +++++++++
 drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
 drivers/dpll/dpll_core.h           | 176 ++++++
 drivers/dpll/dpll_netlink.c        | 963 +++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h        |  24 +
 drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
 drivers/ptp/Kconfig                |   1 +
 drivers/ptp/ptp_ocp.c              | 123 ++--
 include/linux/dpll.h               | 261 ++++++++
 include/linux/dpll_attr.h          | 433 +++++++++++++
 include/uapi/linux/dpll.h          | 263 ++++++++
 18 files changed, 4002 insertions(+), 37 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_attr.c
 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/dpll/dpll_pin_attr.c
 create mode 100644 include/linux/dpll.h
 create mode 100644 include/linux/dpll_attr.h
 create mode 100644 include/uapi/linux/dpll.h

-- 
2.27.0


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

* [RFC PATCH v4 1/4] dpll: add dpll_attr/dpll_pin_attr helper classes
  2022-11-29 21:37 [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Vadim Fedorenko
@ 2022-11-29 21:37 ` Vadim Fedorenko
  2022-11-29 21:37 ` [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions Vadim Fedorenko
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 87+ messages in thread
From: Vadim Fedorenko @ 2022-11-29 21:37 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, linux-arm-kernel, linux-clk, Michal Michalik

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

Classes designed for easy exchange of dpll configuration values
between dpll_netlink, dpll_core and drivers implementing dpll
subsystem.

``dpll_attr`` is designed to store/pass/validate attributes related to
dpll device. ``dpll_pin_attr`` designed for same reason but for
dpll_pin object related values.

All possible attributes for dpll objects are stored in hermetic class
with access only with API functions.

Each attribute is validated on corresponding set function.
If value was not set before, the call to get attribute value either
returns error or unspecified value, depending on the attribute.
The one might also check validaity of any attribute.

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>
---
 drivers/dpll/dpll_attr.c     | 278 +++++++++++++++++++++
 drivers/dpll/dpll_pin_attr.c | 456 +++++++++++++++++++++++++++++++++++
 include/linux/dpll_attr.h    | 433 +++++++++++++++++++++++++++++++++
 3 files changed, 1167 insertions(+)
 create mode 100644 drivers/dpll/dpll_attr.c
 create mode 100644 drivers/dpll/dpll_pin_attr.c
 create mode 100644 include/linux/dpll_attr.h

diff --git a/drivers/dpll/dpll_attr.c b/drivers/dpll/dpll_attr.c
new file mode 100644
index 000000000000..9cf957978ff5
--- /dev/null
+++ b/drivers/dpll/dpll_attr.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  dpll_attr.c - dpll attributes handling helper class.
+ *
+ *  Copyright (c) 2022, Intel Corporation.
+ */
+
+#include <linux/dpll.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+
+struct dpll_attr {
+	unsigned long valid_mask;
+	enum dpll_lock_status lock_status;
+	s32 temp;
+	u32 source_pin_idx;
+	enum dpll_mode mode;
+	unsigned long mode_supported_mask;
+	unsigned int netifindex;
+};
+
+static const int MAX_BITS = BITS_PER_TYPE(unsigned long);
+
+struct dpll_attr *dpll_attr_alloc(void)
+{
+	return kzalloc(sizeof(struct dpll_attr), GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(dpll_attr_alloc);
+
+void dpll_attr_free(struct dpll_attr *attr)
+{
+	kfree(attr);
+}
+EXPORT_SYMBOL_GPL(dpll_attr_free);
+
+void dpll_attr_clear(struct dpll_attr *attr)
+{
+	memset(attr, 0, sizeof(*attr));
+}
+EXPORT_SYMBOL_GPL(dpll_attr_clear);
+
+bool dpll_attr_valid(enum dplla attr_id, const struct dpll_attr *attr)
+{
+	if (!attr)
+		return false;
+	if (attr_id > 0 && attr_id < BITS_PER_LONG)
+		return test_bit(attr_id, &attr->valid_mask);
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_valid);
+
+int
+dpll_attr_copy(struct dpll_attr *dst, const struct dpll_attr *src)
+{
+	if (!src || !dst)
+		return -EFAULT;
+	memcpy(dst, src, sizeof(*dst));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_copy);
+
+static inline bool dpll_lock_status_valid(enum dpll_lock_status status)
+{
+	if (status >= DPLL_LOCK_STATUS_UNSPEC &&
+	    status <= DPLL_LOCK_STATUS_MAX)
+		return true;
+
+	return false;
+}
+
+int dpll_attr_lock_status_set(struct dpll_attr *attr,
+			      enum dpll_lock_status status)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_lock_status_valid(status))
+		return -EINVAL;
+
+	attr->lock_status = status;
+	set_bit(DPLLA_LOCK_STATUS, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_lock_status_set);
+
+enum dpll_lock_status dpll_attr_lock_status_get(const struct dpll_attr *attr)
+{
+	if (!dpll_attr_valid(DPLLA_LOCK_STATUS, attr))
+		return DPLL_LOCK_STATUS_UNSPEC;
+
+	return attr->lock_status;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_lock_status_get);
+
+int dpll_attr_temp_set(struct dpll_attr *attr, s32 temp)
+{
+	if (!attr)
+		return -EFAULT;
+
+	attr->temp = temp;
+	set_bit(DPLLA_TEMP, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_temp_set);
+
+int dpll_attr_temp_get(const struct dpll_attr *attr, s32 *temp)
+{
+	if (!attr || !temp)
+		return -EFAULT;
+	if (!dpll_attr_valid(DPLLA_TEMP, attr))
+		return -EINVAL;
+
+	*temp = attr->temp;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_temp_get);
+
+int dpll_attr_source_idx_set(struct dpll_attr *attr, u32 source_idx)
+{
+	if (!attr)
+		return -EFAULT;
+
+	attr->source_pin_idx = source_idx;
+	set_bit(DPLLA_SOURCE_PIN_IDX, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_source_idx_set);
+
+int dpll_attr_source_idx_get(const struct dpll_attr *attr, u32 *source_idx)
+{
+	if (!attr || !source_idx)
+		return -EFAULT;
+	if (!dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr))
+		return -EINVAL;
+
+	*source_idx = attr->source_pin_idx;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_source_idx_get);
+
+static inline bool dpll_mode_valid(enum dpll_mode mode)
+{
+	if (mode >= DPLL_MODE_UNSPEC &&
+	    mode <= DPLL_MODE_MAX)
+		return true;
+
+	return false;
+}
+
+int dpll_attr_mode_set(struct dpll_attr *attr, enum dpll_mode mode)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_mode_valid(mode))
+		return -EINVAL;
+
+	attr->mode = mode;
+	set_bit(DPLLA_MODE, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_mode_set);
+
+enum dpll_mode dpll_attr_mode_get(const struct dpll_attr *attr)
+{
+	if (!attr || !dpll_attr_valid(DPLLA_MODE, attr))
+		return DPLL_MODE_UNSPEC;
+
+	return attr->mode;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_mode_get);
+
+int dpll_attr_mode_supported_set(struct dpll_attr *attr, enum dpll_mode mode)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_mode_valid(mode))
+		return -EINVAL;
+
+	set_bit(mode, &attr->mode_supported_mask);
+	set_bit(DPLLA_MODE_SUPPORTED, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_mode_supported_set);
+
+bool dpll_attr_mode_supported(const struct dpll_attr *attr,
+			      enum dpll_mode mode)
+{
+	if (!dpll_mode_valid(mode))
+		return false;
+	if (!dpll_attr_valid(DPLLA_MODE_SUPPORTED, attr))
+		return false;
+
+	return test_bit(mode, &attr->mode_supported_mask);
+}
+EXPORT_SYMBOL_GPL(dpll_attr_mode_supported);
+
+int dpll_attr_netifindex_set(struct dpll_attr *attr, unsigned int netifindex)
+{
+	if (!attr)
+		return -EFAULT;
+
+	attr->netifindex = netifindex;
+	set_bit(DPLLA_NETIFINDEX, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_netifindex_set);
+
+int dpll_attr_netifindex_get(const struct dpll_attr *attr,
+			     unsigned int *netifindex)
+{
+	if (!dpll_attr_valid(DPLLA_NETIFINDEX, attr))
+		return -EINVAL;
+
+	*netifindex = attr->netifindex;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_netifindex_get);
+
+static bool dpll_attr_changed(const enum dplla attr_id,
+			      struct dpll_attr *new,
+			      struct dpll_attr *old)
+{
+	if (dpll_attr_valid(attr_id, new)) {
+		if (dpll_attr_valid(attr_id, old)) {
+			switch (attr_id) {
+			case DPLLA_MODE:
+				if (new->mode != old->mode)
+					return true;
+				break;
+			case DPLLA_SOURCE_PIN_IDX:
+				if (new->source_pin_idx != old->source_pin_idx)
+					return true;
+				break;
+			default:
+				return false;
+			}
+		} else {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+int dpll_attr_delta(struct dpll_attr *delta, struct dpll_attr *new,
+		    struct dpll_attr *old)
+{
+	int ret = -EINVAL;
+
+	if (!delta || !new || !old)
+		return -EFAULT;
+
+	dpll_attr_clear(delta);
+
+	if (dpll_attr_changed(DPLLA_MODE, new, old)) {
+		ret = dpll_attr_mode_set(delta, new->mode);
+		if (ret)
+			return ret;
+	}
+	if (dpll_attr_changed(DPLLA_SOURCE_PIN_IDX, new, old)) {
+		ret = dpll_attr_source_idx_set(delta, new->source_pin_idx);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_attr_delta);
diff --git a/drivers/dpll/dpll_pin_attr.c b/drivers/dpll/dpll_pin_attr.c
new file mode 100644
index 000000000000..bf57476228af
--- /dev/null
+++ b/drivers/dpll/dpll_pin_attr.c
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  dpll_pin_attr.c - Pin attribute handling helper class.
+ *
+ *  Copyright (c) 2022, Intel Corporation.
+ */
+
+#include <linux/dpll.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+
+struct dpll_pin_attr {
+	unsigned long valid_mask;
+	enum dpll_pin_type type;
+	unsigned long types_supported_mask;
+	enum dpll_pin_signal_type signal_type;
+	unsigned long signal_types_supported_mask;
+	u32 custom_freq;
+	unsigned long state_mask;
+	unsigned long state_supported_mask;
+	u32 prio;
+	unsigned int netifindex;
+};
+
+static const int MAX_BITS = BITS_PER_TYPE(unsigned long);
+
+struct dpll_pin_attr *dpll_pin_attr_alloc(void)
+{
+	return kzalloc(sizeof(struct dpll_pin_attr), GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_alloc);
+
+void dpll_pin_attr_free(struct dpll_pin_attr *attr)
+{
+	kfree(attr);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_free);
+
+void dpll_pin_attr_clear(struct dpll_pin_attr *attr)
+{
+	memset(attr, 0, sizeof(*attr));
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_clear);
+
+bool dpll_pin_attr_valid(enum dplla attr_id, const struct dpll_pin_attr *attr)
+{
+	if (!attr)
+		return false;
+	if (attr_id > 0 && attr_id < BITS_PER_LONG)
+		return test_bit(attr_id, &attr->valid_mask);
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_valid);
+
+int
+dpll_pin_attr_copy(struct dpll_pin_attr *dst, const struct dpll_pin_attr *src)
+{
+	if (!src || !dst)
+		return -EFAULT;
+	memcpy(dst, src, sizeof(*dst));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_copy);
+
+static inline bool dpll_pin_type_valid(enum dpll_pin_type type)
+{
+	if (type >= DPLL_PIN_TYPE_UNSPEC && type <= DPLL_PIN_TYPE_MAX)
+		return true;
+
+	return false;
+}
+
+int dpll_pin_attr_type_set(struct dpll_pin_attr *attr, enum dpll_pin_type type)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_pin_type_valid(type))
+		return -EINVAL;
+
+	attr->type = type;
+	set_bit(DPLLA_PIN_TYPE, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_type_set);
+
+enum dpll_pin_type dpll_pin_attr_type_get(const struct dpll_pin_attr *attr)
+{
+	if (!dpll_pin_attr_valid(DPLLA_PIN_TYPE, attr))
+		return DPLL_PIN_TYPE_UNSPEC;
+
+	return attr->type;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_type_get);
+
+int dpll_pin_attr_type_supported_set(struct dpll_pin_attr *attr,
+				     enum dpll_pin_type type)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_pin_type_valid(type))
+		return -EINVAL;
+
+	set_bit(type, &attr->types_supported_mask);
+	set_bit(DPLLA_PIN_TYPE_SUPPORTED, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_type_supported_set);
+
+bool dpll_pin_attr_type_supported(const struct dpll_pin_attr *attr,
+				     enum dpll_pin_type type)
+{
+	if (!dpll_pin_type_valid(type))
+		return false;
+	if (!dpll_pin_attr_valid(DPLLA_PIN_TYPE_SUPPORTED, attr))
+		return false;
+
+	return test_bit(type, &attr->types_supported_mask);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_type_supported);
+
+static inline bool dpll_pin_signal_type_valid(enum dpll_pin_signal_type type)
+{
+	if (type >= DPLL_PIN_SIGNAL_TYPE_UNSPEC &&
+	    type <= DPLL_PIN_SIGNAL_TYPE_MAX)
+		return true;
+
+	return false;
+}
+
+int dpll_pin_attr_signal_type_set(struct dpll_pin_attr *attr,
+				  enum dpll_pin_signal_type type)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_pin_signal_type_valid(type))
+		return -EINVAL;
+
+	attr->signal_type = type;
+	set_bit(DPLLA_PIN_SIGNAL_TYPE, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_signal_type_set);
+
+enum dpll_pin_signal_type
+dpll_pin_attr_signal_type_get(const struct dpll_pin_attr *attr)
+{
+	if (!dpll_pin_attr_valid(DPLLA_PIN_SIGNAL_TYPE, attr))
+		return DPLL_PIN_SIGNAL_TYPE_UNSPEC;
+
+	return attr->signal_type;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_signal_type_get);
+
+int dpll_pin_attr_signal_type_supported_set(struct dpll_pin_attr *attr,
+					    enum dpll_pin_signal_type type)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_pin_signal_type_valid(type))
+		return -EINVAL;
+
+	set_bit(type, &attr->signal_types_supported_mask);
+	set_bit(DPLLA_PIN_SIGNAL_TYPE_SUPPORTED, &attr->valid_mask);
+
+	return 0;
+
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_signal_type_supported_set);
+
+bool dpll_pin_attr_signal_type_supported(const struct dpll_pin_attr *attr,
+					    enum dpll_pin_signal_type type)
+{
+	if (!dpll_pin_signal_type_valid(type))
+		return false;
+	if (!dpll_pin_attr_valid(DPLLA_PIN_SIGNAL_TYPE_SUPPORTED, attr))
+		return false;
+
+	return test_bit(type, &attr->signal_types_supported_mask);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_signal_type_supported);
+
+int dpll_pin_attr_custom_freq_set(struct dpll_pin_attr *attr, u32 freq)
+{
+	if (!attr)
+		return -EFAULT;
+
+	attr->custom_freq = freq;
+	set_bit(DPLLA_PIN_CUSTOM_FREQ, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_custom_freq_set);
+
+int dpll_pin_attr_custom_freq_get(const struct dpll_pin_attr *attr, u32 *freq)
+{
+	if (!attr || !freq)
+		return -EFAULT;
+	if (!test_bit(DPLLA_PIN_CUSTOM_FREQ, &attr->valid_mask))
+		return -EINVAL;
+
+	*freq = attr->custom_freq;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_custom_freq_get);
+
+static inline bool dpll_pin_state_valid(enum dpll_pin_state state)
+{
+	if (state >= DPLL_PIN_STATE_UNSPEC &&
+	    state <= DPLL_PIN_STATE_MAX)
+		return true;
+
+	return false;
+}
+
+int dpll_pin_attr_state_set(struct dpll_pin_attr *attr,
+			    enum dpll_pin_state state)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_pin_state_valid(state))
+		return -EINVAL;
+	if (state == DPLL_PIN_STATE_CONNECTED) {
+		if (test_bit(DPLL_PIN_STATE_DISCONNECTED, &attr->state_mask))
+			return -EINVAL;
+	} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
+		if (test_bit(DPLL_PIN_STATE_CONNECTED, &attr->state_mask))
+			return -EINVAL;
+	}
+
+	set_bit(state, &attr->state_mask);
+	set_bit(DPLLA_PIN_STATE, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_state_set);
+
+bool dpll_pin_attr_state_enabled(const struct dpll_pin_attr *attr,
+				 enum dpll_pin_state state)
+{
+	if (!dpll_pin_state_valid(state))
+		return false;
+	if (!dpll_pin_attr_valid(DPLLA_PIN_STATE, attr))
+		return false;
+
+	return test_bit(state, &attr->state_mask);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_state_enabled);
+
+int dpll_pin_attr_state_supported_set(struct dpll_pin_attr *attr,
+				      enum dpll_pin_state state)
+{
+	if (!attr)
+		return -EFAULT;
+	if (!dpll_pin_state_valid(state))
+		return -EINVAL;
+
+	set_bit(state, &attr->state_supported_mask);
+	set_bit(DPLLA_PIN_STATE_SUPPORTED, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_state_supported_set);
+
+bool dpll_pin_attr_state_supported(const struct dpll_pin_attr *attr,
+				   enum dpll_pin_state state)
+{
+	if (!dpll_pin_state_valid(state))
+		return false;
+	if (!dpll_pin_attr_valid(DPLLA_PIN_STATE_SUPPORTED, attr))
+		return false;
+
+	return test_bit(state, &attr->state_supported_mask);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_state_supported);
+
+int dpll_pin_attr_prio_set(struct dpll_pin_attr *attr, u32 prio)
+{
+	if (!attr)
+		return -EFAULT;
+	if (prio > PIN_PRIO_LOWEST)
+		return -EINVAL;
+
+	attr->prio = prio;
+	set_bit(DPLLA_PIN_PRIO, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_prio_set);
+
+int dpll_pin_attr_prio_get(const struct dpll_pin_attr *attr, u32 *prio)
+{
+	if (!attr || !prio)
+		return -EFAULT;
+	if (!dpll_pin_attr_valid(DPLLA_PIN_PRIO, attr))
+		return -EINVAL;
+
+	*prio = attr->prio;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_prio_get);
+
+int dpll_pin_attr_netifindex_set(struct dpll_pin_attr *attr, unsigned int netifindex)
+{
+	if (!attr)
+		return -EFAULT;
+
+	attr->netifindex = netifindex;
+	set_bit(DPLLA_PIN_NETIFINDEX, &attr->valid_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_netifindex_set);
+
+int dpll_pin_attr_netifindex_get(const struct dpll_pin_attr *attr,
+				 unsigned int *netifindex)
+{
+	if (!dpll_pin_attr_valid(DPLLA_PIN_NETIFINDEX, attr))
+		return -EINVAL;
+
+	*netifindex = attr->netifindex;
+	return true;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_netifindex_get);
+
+static bool dpll_pin_attr_changed(const enum dplla attr_id,
+				  struct dpll_pin_attr *new,
+				  struct dpll_pin_attr *old)
+{
+	if (dpll_pin_attr_valid(attr_id, new)) {
+		if (dpll_pin_attr_valid(attr_id, old)) {
+			switch (attr_id) {
+			case DPLLA_PIN_TYPE:
+				if (new->type != old->type)
+					return true;
+				break;
+			case DPLLA_PIN_SIGNAL_TYPE:
+				if (new->signal_type != old->signal_type)
+					return true;
+				break;
+			case DPLLA_PIN_CUSTOM_FREQ:
+				if (new->custom_freq != old->custom_freq)
+					return true;
+				break;
+			case DPLLA_PIN_STATE:
+				if (new->state_mask != old->state_mask)
+					return true;
+				break;
+			case DPLLA_PIN_PRIO:
+				if (new->prio != old->prio)
+					return true;
+				break;
+			default:
+				return false;
+			}
+		} else {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+int dpll_pin_attr_delta(struct dpll_pin_attr *delta, struct dpll_pin_attr *new,
+			struct dpll_pin_attr *old)
+{
+	int ret = -EINVAL;
+
+	if (!delta || !new || !old)
+		return -EFAULT;
+
+	dpll_pin_attr_clear(delta);
+
+	if (dpll_pin_attr_changed(DPLLA_PIN_TYPE, new, old)) {
+		ret = dpll_pin_attr_type_set(delta, new->type);
+		if (ret)
+			return ret;
+	}
+	if (dpll_pin_attr_changed(DPLLA_PIN_SIGNAL_TYPE, new, old)) {
+		ret = dpll_pin_attr_signal_type_set(delta, new->signal_type);
+		if (ret)
+			return ret;
+	}
+	if (dpll_pin_attr_changed(DPLLA_PIN_CUSTOM_FREQ, new, old)) {
+		ret = dpll_pin_attr_custom_freq_set(delta, new->custom_freq);
+		if (ret)
+			return ret;
+	}
+	if (dpll_pin_attr_changed(DPLLA_PIN_STATE, new, old)) {
+		enum dpll_pin_state i;
+
+		for (i = DPLL_PIN_STATE_UNSPEC + 1;
+		     i <= DPLL_PIN_STATE_MAX; i++)
+			if (test_bit(i, &new->state_mask)) {
+				ret = dpll_pin_attr_state_set(delta, i);
+				if (ret)
+					return ret;
+			}
+	}
+	if (dpll_pin_attr_changed(DPLLA_PIN_PRIO, new, old)) {
+		ret = dpll_pin_attr_prio_set(delta, new->prio);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_delta);
+
+int dpll_pin_attr_prep_common(struct dpll_pin_attr *common,
+			      const struct dpll_pin_attr *reference)
+{
+	if (!common || !reference)
+		return -EFAULT;
+	dpll_pin_attr_clear(common);
+	if (dpll_pin_attr_valid(DPLLA_PIN_TYPE, reference)) {
+		common->type = reference->type;
+		set_bit(DPLLA_PIN_TYPE, &common->valid_mask);
+	}
+	if (dpll_pin_attr_valid(DPLLA_PIN_SIGNAL_TYPE, reference)) {
+		common->signal_type = reference->signal_type;
+		set_bit(DPLLA_PIN_SIGNAL_TYPE, &common->valid_mask);
+	}
+	if (dpll_pin_attr_valid(DPLLA_PIN_CUSTOM_FREQ, reference)) {
+		common->custom_freq = reference->custom_freq;
+		set_bit(DPLLA_PIN_CUSTOM_FREQ, &common->valid_mask);
+	}
+	if (dpll_pin_attr_valid(DPLLA_PIN_STATE, reference)) {
+		common->state_mask = reference->state_mask;
+		set_bit(DPLLA_PIN_STATE, &common->valid_mask);
+	}
+	return common->valid_mask ? PIN_ATTR_CHANGE : 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_prep_common);
+
+
+int dpll_pin_attr_prep_exclusive(struct dpll_pin_attr *exclusive,
+				 const struct dpll_pin_attr *reference)
+{
+	if (!exclusive || !reference)
+		return -EFAULT;
+	dpll_pin_attr_clear(exclusive);
+	if (dpll_pin_attr_valid(DPLLA_PIN_PRIO, reference)) {
+		exclusive->prio = reference->prio;
+		set_bit(DPLLA_PIN_PRIO, &exclusive->valid_mask);
+	}
+	return exclusive->valid_mask ? PIN_ATTR_CHANGE : 0;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_attr_prep_exclusive);
+
diff --git a/include/linux/dpll_attr.h b/include/linux/dpll_attr.h
new file mode 100644
index 000000000000..fe5e2188ca0b
--- /dev/null
+++ b/include/linux/dpll_attr.h
@@ -0,0 +1,433 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  dpll_attr.h - Header for attribute handling helper class.
+ *
+ *  Copyright (c) 2022, Intel Corporation.
+ */
+
+#ifndef __DPLL_ATTR_H__
+#define __DPLL_ATTR_H__
+
+#include <linux/types.h>
+
+struct dpll_attr;
+struct dpll_pin_attr;
+
+#define PIN_PRIO_HIGHEST	0
+#define PIN_PRIO_LOWEST		0xff
+#define PIN_ATTR_CHANGE		1
+
+/**
+ * dpll_attr_alloc - allocate a dpll attributes struct
+ *
+ * Return: pointer if succeeded, NULL otherwise.
+ */
+struct dpll_attr *dpll_attr_alloc(void);
+
+/**
+ * dpll_attr_free - frees a dpll attributes struct
+ * @attr: structure with dpll attributes
+ */
+void dpll_attr_free(struct dpll_attr *attr);
+
+/**
+ * dpll_attr_clear - clears a dpll attributes struct
+ * @attr: structure with dpll attributes
+ */
+void dpll_attr_clear(struct dpll_attr *attr);
+
+/**
+ * dpll_attr_valid - checks if a attribute is valid
+ * @attr_id: attribute to be checked
+ * @attr: structure with dpll attributes
+ *
+ * Checks if the attribute has been set before and if stored value is valid.
+ * Return: true if valid, false otherwise.
+ */
+bool dpll_attr_valid(enum dplla attr_id, const struct dpll_attr *attr);
+
+/**
+ * dpll_attr_copy - create a copy of the dpll attributes structure
+ * @dst: destination structure with dpll attributes
+ * @src: source structure with dpll attributes
+ *
+ * Memory needs to be allocated before calling this function.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int
+dpll_attr_copy(struct dpll_attr *dst, const struct dpll_attr *src);
+
+/**
+ * dpll_attr_lock_status_set - set the lock status in the attributes
+ * @attr: structure with dpll attributes
+ * @status: dpll lock status to be set
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_lock_status_set(struct dpll_attr *attr,
+			      enum dpll_lock_status status);
+
+/**
+ * dpll_attr_lock_status_get - get the lock status from the attributes
+ * @attr: structure with dpll attributes
+ *
+ * Return: dpll lock status
+ */
+enum dpll_lock_status
+dpll_attr_lock_status_get(const struct dpll_attr *attr);
+
+/**
+ * dpll_attr_temp_set - set the temperature in the attributes
+ * @attr: structure with dpll attributes
+ * @temp: temperature to be set
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_temp_set(struct dpll_attr *attr, s32 temp);
+
+/**
+ * dpll_attr_temp_get - get the temperature from the attributes
+ * @attr: structure with dpll attributes
+ * @temp: temperature (out)
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_temp_get(const struct dpll_attr *attr, s32 *temp);
+
+/**
+ * dpll_attr_source_idx_set - set the source id in the attributes
+ * @attr: structure with dpll attributes
+ * @source_idx: source id to be set
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_source_idx_set(struct dpll_attr *attr, u32 source_idx);
+
+/**
+ * dpll_attr_source_idx_get - get the source id from the attributes
+ * @attr: structure with dpll attributes
+ * @source_idx: source id (out)
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_source_idx_get(const struct dpll_attr *attr, u32 *source_idx);
+
+/**
+ * dpll_attr_mode_set - set the dpll mode in the attributes
+ * @attr: structure with dpll attributes
+ * @mode: dpll mode to be set
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_mode_set(struct dpll_attr *attr, enum dpll_mode mode);
+
+/**
+ * dpll_attr_mode_get - get the dpll mode from the attributes
+ * @attr: structure with dpll attributes
+ *
+ * Return: dpll mode.
+ */
+enum dpll_mode dpll_attr_mode_get(const struct dpll_attr *attr);
+
+/**
+ * dpll_attr_mode_set - set the dpll supported mode in the attributes
+ * @attr: structure with dpll attributes
+ * @mode: dpll mode to be set in supported modes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_mode_supported_set(struct dpll_attr *attr, enum dpll_mode mode);
+
+/**
+ * dpll_attr_mode_supported - check if the dpll mode is supported
+ * @attr: structure with dpll attributes
+ * @mode: dpll mode to be checked
+ *
+ * Return: true if mode supported, false otherwise.
+ */
+bool dpll_attr_mode_supported(const struct dpll_attr *attr,
+			      enum dpll_mode mode);
+
+/**
+ * dpll_attr_netifindex_set - set the netifindex in the attributes
+ * @attr: structure with dpll attributes
+ * @netifindex: parameter to be set in attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_netifindex_set(struct dpll_attr *attr, unsigned int netifindex);
+
+/**
+ * dpll_attr_netifindex_get - get the netifindex from the attributes
+ * @attr: structure with dpll attributes
+ * @netifindex: retrieved parameter
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_netifindex_get(const struct dpll_attr *attr,
+			     unsigned int *netifindex);
+
+/**
+ * dpll_attr_delta - calculate the difference between two dpll attribute sets
+ * @delta: structure with delta of dpll attributes
+ * @new: structure with new dpll attributes
+ * @old: structure with old dpll attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_attr_delta(struct dpll_attr *delta, struct dpll_attr *new,
+		    struct dpll_attr *old);
+
+/**
+ * dpll_pin_attr_alloc - allocate a dpll pin attributes struct
+ *
+ * Return: pointer if succeeded, NULL otherwise.
+ */
+struct dpll_pin_attr *dpll_pin_attr_alloc(void);
+
+/**
+ * dpll_pin_attr_free - frees a dpll pin attributes struct
+ * @attr: structure with dpll pin attributes
+ */
+void dpll_pin_attr_free(struct dpll_pin_attr *attr);
+
+/**
+ * dpll_pin_attr_clear - clears a dpll pin attributes struct
+ * @attr: structure with dpll attributes
+ */
+void dpll_pin_attr_clear(struct dpll_pin_attr *attr);
+
+/**
+ * dpll_pin_attr_valid - checks if a pin attribute is valid
+ * @attr_id: attribute to be checked
+ * @attr: structure with dpll pin attributes
+ *
+ * Checks if the attribute has been set before and if stored value is valid.
+ * Return: true if valid, false otherwise.
+ */
+bool dpll_pin_attr_valid(enum dplla attr_id, const struct dpll_pin_attr *attr);
+
+/**
+ * dpll_pin_attr_copy - create a copy of the dpll pin attributes structure
+ * @dst: destination structure with dpll pin attributes
+ * @src: source structure with dpll pin attributes
+ *
+ * Memory needs to be allocated before calling this function.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int
+dpll_pin_attr_copy(struct dpll_pin_attr *dst, const struct dpll_pin_attr *src);
+
+/**
+ * dpll_pin_attr_type_set - set the pin type in the attributes
+ * @attr: structure with dpll pin attributes
+ * @type: parameter to be set in attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_type_set(struct dpll_pin_attr *attr, enum dpll_pin_type type);
+
+/**
+ * dpll_pin_attr_type_get - get the pin type from the attributes
+ * @attr: structure with dpll pin attributes
+ *
+ * Return: dpll pin type.
+ */
+enum dpll_pin_type dpll_pin_attr_type_get(const struct dpll_pin_attr *attr);
+
+/**
+ * dpll_pin_attr_type_supported_set - set the dpll pin supported type in the
+ * attributes
+ * @attr: structure with dpll attributes
+ * @type: pin type to be set in supported modes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_type_supported_set(struct dpll_pin_attr *attr,
+				     enum dpll_pin_type type);
+
+/**
+ * dpll_pin_attr_type_supported - check if the pin type is supported
+ * @attr: structure with dpll attributes
+ * @type: dpll mode to be checked
+ *
+ * Return: true if type supported, false otherwise.
+ */
+bool dpll_pin_attr_type_supported(const struct dpll_pin_attr *attr,
+				     enum dpll_pin_type type);
+
+/**
+ * dpll_pin_attr_signal_type_set - set the pin signal type in the attributes
+ * @attr: structure with dpll attributes
+ * @type: pin signal type to be set in supported modes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_signal_type_set(struct dpll_pin_attr *attr,
+				  enum dpll_pin_signal_type type);
+
+/**
+ * dpll_pin_attr_signal_type_get - get the pin signal type from the attributes
+ * @attr: structure with dpll pin attributes
+ *
+ * Return: pin signal type.
+ */
+enum dpll_pin_signal_type
+dpll_pin_attr_signal_type_get(const struct dpll_pin_attr *attr);
+
+/**
+ * dpll_pin_attr_signal_type_supported_set - set the dpll pin supported signal
+ * type in the attributes
+ * @attr: structure with dpll attributes
+ * @type: pin signal type to be set in supported types
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_signal_type_supported_set(struct dpll_pin_attr *attr,
+					    enum dpll_pin_signal_type type);
+
+/**
+ * dpll_pin_attr_signal_type_supported - check if the pin signal type is
+ * supported
+ * @attr: structure with dpll attributes
+ * @type: pin signal type to be checked
+ *
+ * Return: true if type supported, false otherwise.
+ */
+bool dpll_pin_attr_signal_type_supported(const struct dpll_pin_attr *attr,
+					    enum dpll_pin_signal_type type);
+
+/**
+ * dpll_pin_attr_custom_freq_set - set the custom frequency in the attributes
+ * @attr: structure with dpll pin attributes
+ * @freq: parameter to be set in attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_custom_freq_set(struct dpll_pin_attr *attr, u32 freq);
+
+/**
+ * dpll_pin_attr_custom_freq_get - get the pin type from the attributes
+ * @attr: structure with dpll pin attributes
+ * @freq: parameter to be retrieved
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_custom_freq_get(const struct dpll_pin_attr *attr, u32 *freq);
+
+/**
+ * dpll_pin_attr_state_set - set the pin state the attributes
+ * @attr: structure with dpll pin attributes
+ * @state: parameter to be set in attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_state_set(struct dpll_pin_attr *attr,
+			    enum dpll_pin_state state);
+
+/**
+ * dpll_pin_attr_state_enabled - check if state is enabled
+ * @attr: structure with dpll pin attributes
+ * @state: parameter to be checked in attributes
+ *
+ * Return: true if enabled, false otherwise.
+ */
+bool dpll_pin_attr_state_enabled(const struct dpll_pin_attr *attr,
+				 enum dpll_pin_state state);
+
+/**
+ * dpll_pin_attr_state_supported_set - set the supported pin state in the
+ * attributes
+ * @attr: structure with dpll attributes
+ * @state: pin state to be set in supported types
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_state_supported_set(struct dpll_pin_attr *attr,
+				      enum dpll_pin_state state);
+
+/**
+ * dpll_pin_attr_state_supported - check if the pin state is supported
+ * @attr: structure with dpll attributes
+ * @state: pin signal type to be checked
+ *
+ * Return: true if state supported, false otherwise.
+ */
+bool dpll_pin_attr_state_supported(const struct dpll_pin_attr *attr,
+				   enum dpll_pin_state state);
+
+/**
+ * dpll_pin_attr_prio_set - set the pin priority in the attributes
+ * @attr: structure with dpll pin attributes
+ * @prio: parameter to be set in attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_prio_set(struct dpll_pin_attr *attr, u32 prio);
+
+/**
+ * dpll_pin_attr_prio_get - get the pin priority from the attributes
+ * @attr: structure with dpll pin attributes
+ * @prio: parameter to be retrieved
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_prio_get(const struct dpll_pin_attr *attr, u32 *prio);
+
+/**
+ * dpll_pin_attr_netifindex_set - set the pin netifindex in the attributes
+ * @attr: structure with dpll pin attributes
+ * @netifindex: parameter to be set in attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_netifindex_set(struct dpll_pin_attr *attr,
+				 unsigned int netifindex);
+
+/**
+ * dpll_pin_attr_netifindex_get - get the pin netifindex from the attributes
+ * @attr: structure with dpll pin attributes
+ * @netifindex: parameter to be retrieved
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_netifindex_get(const struct dpll_pin_attr *attr,
+				 unsigned int *netifindex);
+
+/**
+ * dpll_attr_delta - calculate the difference between two dpll pin attribute sets
+ * @delta: structure with delta of dpll pin attributes
+ * @new: structure with new dpll pin attributes
+ * @old: structure with old dpll pin attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_delta(struct dpll_pin_attr *delta, struct dpll_pin_attr *new,
+			struct dpll_pin_attr *old);
+
+/**
+ * dpll_pin_attr_prep_common - calculate the common dpll pin attributes
+ * @common: structure with common dpll pin attributes
+ * @reference: referenced structure with dpll pin attributes
+ *
+ * Some of the pin attributes applies to all DPLLs and other are exclusive.
+ * This function calculates if any of the common pin attributes are set.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_prep_common(struct dpll_pin_attr *common,
+			      const struct dpll_pin_attr *reference);
+
+/**
+ * dpll_pin_attr_prep_exclusive - calculate the exclusive dpll pin attributes
+ * @exclusive: structure with common dpll pin attributes
+ * @reference: referenced structure with dpll pin attributes
+ *
+ * Some of the pin attributes applies to all DPLLs and other are exclusive.
+ * This function calculates if any of the exclusive pin attributes are set.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_attr_prep_exclusive(struct dpll_pin_attr *exclusive,
+				 const struct dpll_pin_attr *reference);
+
+
+#endif /* __DPLL_ATTR_H__ */
-- 
2.27.0


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

* [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-29 21:37 [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Vadim Fedorenko
  2022-11-29 21:37 ` [RFC PATCH v4 1/4] dpll: add dpll_attr/dpll_pin_attr helper classes Vadim Fedorenko
@ 2022-11-29 21:37 ` Vadim Fedorenko
  2022-11-30 15:21   ` Jiri Pirko
  2022-11-30 16:37   ` Jiri Pirko
  2022-11-29 21:37 ` [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface Vadim Fedorenko
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 87+ messages in thread
From: Vadim Fedorenko @ 2022-11-29 21:37 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk,
	Milena Olech, Michal Michalik

From: Vadim Fedorenko <vadfed@fb.com>

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@fb.com>
---
 MAINTAINERS                 |   8 +
 drivers/Kconfig             |   2 +
 drivers/Makefile            |   1 +
 drivers/dpll/Kconfig        |   7 +
 drivers/dpll/Makefile       |  11 +
 drivers/dpll/dpll_core.c    | 760 ++++++++++++++++++++++++++++
 drivers/dpll/dpll_core.h    | 176 +++++++
 drivers/dpll/dpll_netlink.c | 963 ++++++++++++++++++++++++++++++++++++
 drivers/dpll/dpll_netlink.h |  24 +
 include/linux/dpll.h        | 261 ++++++++++
 include/uapi/linux/dpll.h   | 263 ++++++++++
 11 files changed, 2476 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 61fe86968111..79b76cca9620 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6343,6 +6343,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 19ee995bd0ae..a3e00294a995 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -239,4 +239,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..3391deb08014
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DPLL drivers.
+#
+
+obj-$(CONFIG_DPLL)          += dpll_sys.o
+dpll_sys-y                  += dpll_attr.o
+dpll_sys-y                  += dpll_core.o
+dpll_sys-y                  += dpll_netlink.o
+dpll_sys-y                  += dpll_pin_attr.o
+
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..013ac4150583
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,760 @@
+// 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[PIN_DESC_LEN];
+};
+
+/**
+ * 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
+ * @cookie:	unique identifier (cookie) of a dpll
+ * @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;
+	u8 cookie[DPLL_COOKIE_LEN];
+	u8 dev_driver_idx;
+};
+
+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 pin_ref_dpll {
+	struct dpll_device *dpll;
+	struct dpll_pin_ops *ops;
+	void *priv;
+};
+
+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;
+}
+
+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_cookie(u8 cookie[DPLL_COOKIE_LEN],
+					      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 (!memcmp(dpll->cookie, cookie, DPLL_COOKIE_LEN)) {
+			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_cookie);
+
+static void dpll_device_release(struct device *dev)
+{
+	struct dpll_device *dpll;
+
+	dpll = to_dpll_device(dev);
+
+	dpll_device_unregister(dpll);
+	dpll_device_free(dpll);
+}
+
+static struct class dpll_class = {
+	.name = "dpll",
+	.dev_release = dpll_device_release,
+};
+
+static const char *dpll_type_name[__DPLL_TYPE_MAX] = {
+	[DPLL_TYPE_UNSPEC] = "",
+	[DPLL_TYPE_PPS] = "PPS",
+	[DPLL_TYPE_EEC] = "EEC",
+};
+
+static const char *dpll_type_str(enum dpll_type type)
+{
+	if (type >= DPLL_TYPE_UNSPEC && type <= DPLL_TYPE_MAX)
+		return dpll_type_name[type];
+	else
+		return "";
+}
+
+struct dpll_device
+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
+		   const u8 cookie[DPLL_COOKIE_LEN], u8 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 = idx;
+	memcpy(dpll->cookie, cookie, sizeof(dpll->cookie));
+
+	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-%s-%s%d", dev_driver_string(parent),
+		     dev_name(parent), type ? dpll_type_str(type) : "", idx);
+	dpll->priv = priv;
+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
+	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);
+
+void dpll_device_register(struct dpll_device *dpll)
+{
+	ASSERT_DPLL_NOT_REGISTERED(dpll);
+
+	mutex_lock(&dpll_device_xa_lock);
+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+	mutex_unlock(&dpll_device_xa_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_register);
+
+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);
+
+u32 dpll_id(struct dpll_device *dpll)
+{
+	return dpll->id;
+}
+EXPORT_SYMBOL_GPL(dpll_id);
+
+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);
+}
+EXPORT_SYMBOL_GPL(dpll_dev_name);
+
+struct dpll_pin *dpll_pin_alloc(const char *description, size_t desc_len)
+{
+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
+
+	if (!pin)
+		return ERR_PTR(-ENOMEM);
+	if (desc_len > PIN_DESC_LEN)
+		return ERR_PTR(-EINVAL);
+
+	strncpy(pin->description, description, PIN_DESC_LEN);
+	if (desc_len == PIN_DESC_LEN)
+		pin->description[PIN_DESC_LEN - 1] = '\0';
+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
+
+	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, 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 pin_ref_dpll_add(struct dpll_pin *pin, struct dpll_device *dpll,
+			    struct dpll_pin_ops *ops, void *priv)
+{
+	struct pin_ref_dpll *ref, *pos;
+	unsigned long index;
+	u32 idx;
+
+	ref = kzalloc(sizeof(struct pin_ref_dpll), 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 pin_ref_dpll_del(struct dpll_pin *pin, struct dpll_device *dpll)
+{
+	struct pin_ref_dpll *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 = pin_ref_dpll_add(pin, dpll, ops, priv);
+		if (ret)
+			pin_deregister_from_xa(&dpll->pins, pin);
+	}
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_ADD, pin);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_register);
+
+int
+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
+			 struct dpll_device *dpll, u32 pin_idx,
+			 struct dpll_pin_ops *ops, void *priv)
+{
+	struct dpll_pin *pin;
+	int ret;
+
+	mutex_lock(&dpll_pin_owner->lock);
+	pin = dpll_pin_get_by_idx(dpll_pin_owner, pin_idx);
+	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)
+		pin_ref_dpll_del(pin, dpll);
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_DEL, pin);
+
+	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,
+			    struct dpll_pin *parent_pin, struct dpll_pin *pin,
+			    struct dpll_pin_ops *ops, void *priv)
+{
+	int ret;
+
+	if (!parent_pin || !pin)
+		return -EINVAL;
+
+	mutex_lock(&dpll->lock);
+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
+	if (!ret)
+		ret = pin_ref_dpll_add(pin, dpll, ops, priv);
+	if (!ret)
+		pin->parent_pin = parent_pin;
+	mutex_unlock(&dpll->lock);
+	if (!ret)
+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_ADD, pin);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
+
+struct dpll_pin
+*dpll_pin_get_by_description(struct dpll_device *dpll, const char *description)
+{
+	struct dpll_pin *pos, *pin = NULL;
+	unsigned long index;
+
+	mutex_lock(&dpll->lock);
+	xa_for_each(&dpll->pins, index, pos) {
+		if (!strncmp(pos->description, description, PIN_DESC_LEN)) {
+			pin = pos;
+			break;
+		}
+	}
+	mutex_unlock(&dpll->lock);
+
+	return pin;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_get_by_description);
+
+static 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;
+}
+
+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);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_get_by_idx);
+
+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);
+}
+
+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index)
+{
+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
+}
+
+struct dpll_device *dpll_first(unsigned long *index)
+{
+	*index = 0;
+
+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
+}
+
+struct dpll_device *dpll_next(unsigned long *index)
+{
+	return xa_find_after(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
+}
+
+static int
+dpll_notify_pin_change_attr(struct dpll_device *dpll, struct dpll_pin *pin,
+			    const struct dpll_pin_attr *attr)
+{
+	enum dpll_event_change change;
+	int ret = 0;
+
+	if (dpll_pin_attr_valid(DPLLA_PIN_TYPE, attr)) {
+		change = DPLL_CHANGE_PIN_TYPE;
+		ret = dpll_notify_device_change(dpll, change, pin);
+	}
+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_SIGNAL_TYPE, attr)) {
+		change = DPLL_CHANGE_PIN_SIGNAL_TYPE;
+		ret = dpll_notify_device_change(dpll, change, pin);
+	}
+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_CUSTOM_FREQ, attr)) {
+		change = DPLL_CHANGE_PIN_CUSTOM_FREQ;
+		ret = dpll_notify_device_change(dpll, change, pin);
+	}
+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_STATE, attr)) {
+		change = DPLL_CHANGE_PIN_STATE;
+		ret = dpll_notify_device_change(dpll, change, pin);
+	}
+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_PRIO, attr)) {
+		change = DPLL_CHANGE_PIN_PRIO;
+		ret = dpll_notify_device_change(dpll, change, pin);
+	}
+
+	return ret;
+}
+
+static int dpll_notify_device_change_attr(struct dpll_device *dpll,
+					  const struct dpll_attr *attr)
+{
+	int ret = 0;
+
+	if (dpll_attr_valid(DPLLA_MODE, attr))
+		ret = dpll_notify_device_change(dpll, DPLL_CHANGE_MODE, NULL);
+	if (!ret && dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr))
+		ret = dpll_notify_device_change(dpll, DPLL_CHANGE_SOURCE_PIN,
+						NULL);
+	return ret;
+}
+
+static struct pin_ref_dpll
+*dpll_pin_find_ref(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	struct pin_ref_dpll *ref;
+	unsigned long index;
+
+	xa_for_each(&pin->ref_dplls, index, ref) {
+		if (ref->dpll != dpll)
+			continue;
+		else
+			return ref;
+	}
+
+	return NULL;
+}
+
+static int
+dpll_pin_set_attr_single_ref(struct dpll_device *dpll, struct dpll_pin *pin,
+			     const struct dpll_pin_attr *attr)
+{
+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	mutex_lock(&ref->dpll->lock);
+	ret = ref->ops->set(ref->dpll, pin, attr);
+	if (!ret)
+		dpll_notify_pin_change_attr(dpll, pin, attr);
+	mutex_unlock(&ref->dpll->lock);
+
+	return ret;
+}
+
+static int
+dpll_pin_set_attr_all_refs(struct dpll_pin *pin,
+			   const struct dpll_pin_attr *attr)
+{
+	struct pin_ref_dpll *ref;
+	unsigned long index;
+	int ret;
+
+	xa_for_each(&pin->ref_dplls, index, ref) {
+		if (!ref->dpll)
+			return -EFAULT;
+		if (!ref || !ref->ops || !ref->ops->set)
+			return -EOPNOTSUPP;
+		mutex_lock(&ref->dpll->lock);
+		ret = ref->ops->set(ref->dpll, pin, attr);
+		mutex_unlock(&ref->dpll->lock);
+		if (!ret)
+			dpll_notify_pin_change_attr(ref->dpll, pin, attr);
+	}
+
+	return ret;
+}
+
+int dpll_pin_set_attr(struct dpll_device *dpll, struct dpll_pin *pin,
+		      const struct dpll_pin_attr *attr)
+{
+	struct dpll_pin_attr *tmp_attr;
+	int ret;
+
+	tmp_attr = dpll_pin_attr_alloc();
+	if (!tmp_attr)
+		return -ENOMEM;
+	ret = dpll_pin_attr_prep_common(tmp_attr, attr);
+	if (ret < 0)
+		goto tmp_free;
+	if (ret == PIN_ATTR_CHANGE) {
+		ret = dpll_pin_set_attr_all_refs(pin, tmp_attr);
+		if (ret)
+			goto tmp_free;
+	}
+
+	ret = dpll_pin_attr_prep_exclusive(tmp_attr, attr);
+	if (ret < 0)
+		goto tmp_free;
+	if (ret == PIN_ATTR_CHANGE)
+		ret = dpll_pin_set_attr_single_ref(dpll, pin, tmp_attr);
+
+tmp_free:
+	dpll_pin_attr_free(tmp_attr);
+	return ret;
+}
+
+int dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
+		      struct dpll_pin_attr *attr)
+{
+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
+	int ret;
+
+	if (!ref)
+		return -ENODEV;
+	if (!ref->ops || !ref->ops->get)
+		return -EOPNOTSUPP;
+
+	ret = ref->ops->get(dpll, pin, attr);
+	if (ret)
+		return -EAGAIN;
+
+	return ret;
+}
+
+const char *dpll_pin_get_description(struct dpll_pin *pin)
+{
+	return pin->description;
+}
+
+struct dpll_pin *dpll_pin_get_parent(struct dpll_pin *pin)
+{
+	return pin->parent_pin;
+}
+
+int dpll_set_attr(struct dpll_device *dpll, const struct dpll_attr *attr)
+{
+	int ret;
+
+	if (dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr)) {
+		struct pin_ref_dpll *ref;
+		struct dpll_pin *pin;
+		u32 source_idx;
+
+		ret = dpll_attr_source_idx_get(attr, &source_idx);
+		if (ret)
+			return -EINVAL;
+		pin = dpll_pin_get_by_idx(dpll, source_idx);
+		if (!pin)
+			return -ENXIO;
+		ref = dpll_pin_find_ref(dpll, pin);
+		if (!ref || !ref->ops)
+			return -EFAULT;
+		if (!ref->ops->select)
+			return -ENODEV;
+		dpll_lock(ref->dpll);
+		ret = ref->ops->select(ref->dpll, pin);
+		dpll_unlock(ref->dpll);
+		if (ret)
+			return -EINVAL;
+		dpll_notify_device_change_attr(dpll, attr);
+	}
+
+	if (dpll_attr_valid(DPLLA_MODE, attr)) {
+		dpll_lock(dpll);
+		ret = dpll->ops->set(dpll, attr);
+		dpll_unlock(dpll);
+		if (ret)
+			return -EINVAL;
+	}
+	dpll_notify_device_change_attr(dpll, attr);
+
+	return ret;
+}
+
+int dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr)
+{
+	if (!dpll)
+		return -ENODEV;
+	if (!dpll->ops || !dpll->ops->get)
+		return -EOPNOTSUPP;
+	if (dpll->ops->get(dpll, attr))
+		return -EAGAIN;
+
+	return 0;
+}
+
+void dpll_lock(struct dpll_device *dpll)
+{
+	mutex_lock(&dpll->lock);
+}
+
+void dpll_unlock(struct dpll_device *dpll)
+{
+	mutex_unlock(&dpll->lock);
+}
+
+void *dpll_priv(struct dpll_device *dpll)
+{
+	return dpll->priv;
+}
+EXPORT_SYMBOL_GPL(dpll_priv);
+
+void *dpll_pin_priv(struct dpll_device *dpll, struct dpll_pin *pin)
+{
+	struct pin_ref_dpll *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..9cefecdfc47b
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,176 @@
+/* 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)
+
+#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))
+
+
+/**
+ * dpll_device_get_by_id - find dpll device by it's id
+ * @id: dpll id
+ *
+ * Return: dpll_device struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_device_get_by_id(int id);
+
+/**
+ * dpll_device_get_by_name - find dpll device by it's id
+ * @name: dpll name
+ *
+ * Return: dpll_device struct if found, NULL otherwise.
+ */
+struct dpll_device *dpll_device_get_by_name(const char *name);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * dpll_id - return dpll id
+ * @dpll: registered dpll pointer
+ *
+ * Return: dpll id.
+ */
+u32 dpll_id(struct dpll_device *dpll);
+
+/**
+ * dpll_pin_idx - return dpll name
+ * @dpll: registered dpll pointer
+ *
+ * Return: dpll name.
+ */
+const char *dpll_dev_name(struct dpll_device *dpll);
+
+/**
+ * dpll_lock - locks the dpll using internal mutex
+ * @dpll: registered dpll pointer
+ */
+void dpll_lock(struct dpll_device *dpll);
+
+/**
+ * dpll_unlock - unlocks the dpll using internal mutex
+ * @dpll: registered dpll pointer
+ */
+void dpll_unlock(struct dpll_device *dpll);
+
+/**
+ * dpll_set_attr - handler for dpll subsystem: dpll set attributes
+ * @dpll: registered dpll pointer
+ * @attr: dpll attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_set_attr(struct dpll_device *dpll, const struct dpll_attr *attr);
+
+/**
+ * dpll_get_attr - handler for dpll subsystem: dpll get attributes
+ * @dpll: registered dpll pointer
+ * @attr: dpll attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr);
+
+/**
+ * dpll_pin_idx - return dpll id
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ *
+ * Return: dpll id.
+ */
+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
+
+/**
+ * dpll_pin_get_attr - handler for dpll subsystem: dpll pin get attributes
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @attr: dpll pin attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
+		      struct dpll_pin_attr *attr);
+
+/**
+ * dpll_pin_get_description - provide pin's description string
+ * @pin: registered pin pointer
+ *
+ * Return: pointer to a description string.
+ */
+const char *dpll_pin_get_description(struct dpll_pin *pin);
+
+/**
+ * dpll_pin_get_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_get_parent(struct dpll_pin *pin);
+
+/**
+ * dpll_pin_set_attr - handler for dpll subsystem: dpll pin get attributes
+ * @dpll: registered dpll pointer
+ * @pin: registered pin pointer
+ * @attr: dpll pin attributes
+ *
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_set_attr(struct dpll_device *dpll, struct dpll_pin *pin,
+		      const struct dpll_pin_attr *attr);
+
+#endif
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..9a1f682a42ac
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,963 @@
+// 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_DUMP_FILTER]	= { .type = NLA_U32 },
+	[DPLLA_NETIFINDEX]	= { .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_TYPE]	= { .type = NLA_U32 },
+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
+	[DPLLA_PIN_STATE]	= { .type = NLA_U32 },
+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
+};
+
+struct dpll_param {
+	struct netlink_callback *cb;
+	struct sk_buff *msg;
+	struct dpll_device *dpll;
+	struct dpll_pin *pin;
+	enum dpll_event_change change_type;
+};
+
+struct dpll_dump_ctx {
+	int dump_filter;
+};
+
+typedef int (*cb_t)(struct dpll_param *);
+
+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_attr *attr)
+{
+	enum dpll_mode m = dpll_attr_mode_get(attr);
+
+	if (m == DPLL_MODE_UNSPEC)
+		return 0;
+
+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
+}
+
+static int dpll_msg_add_modes_supported(struct sk_buff *msg,
+					const struct dpll_attr *attr)
+{
+	enum dpll_mode i;
+	int  ret = 0;
+
+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
+		if (dpll_attr_mode_supported(attr, i)) {
+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
+			if (ret)
+				return -EMSGSIZE;
+		}
+	}
+
+	return ret;
+}
+
+static int dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_attr *attr)
+{
+	u32 source_idx;
+
+	if (dpll_attr_source_idx_get(attr, &source_idx))
+		return 0;
+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_netifindex(struct sk_buff *msg, struct dpll_attr *attr)
+{
+	unsigned int netifindex; // TODO: Should be u32?
+
+	if (dpll_attr_netifindex_get(attr, &netifindex))
+		return 0;
+	if (nla_put_u32(msg, DPLLA_NETIFINDEX, netifindex))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_attr *attr)
+{
+	enum dpll_lock_status s = dpll_attr_lock_status_get(attr);
+
+	if (s == DPLL_LOCK_STATUS_UNSPEC)
+		return 0;
+	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_attr *attr)
+{
+	s32 temp;
+
+	if (dpll_attr_temp_get(attr, &temp))
+		return 0;
+	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, enum dplla attr,
+				   enum dpll_pin_type type)
+{
+	if (nla_put_s32(msg, attr, type))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_pin_attr *attr)
+{
+	enum dpll_pin_type t = dpll_pin_attr_type_get(attr);
+
+	if (t == DPLL_PIN_TYPE_UNSPEC)
+		return 0;
+
+	return __dpll_msg_add_pin_type(msg, DPLLA_PIN_TYPE, t);
+}
+
+static int dpll_msg_add_pin_types_supported(struct sk_buff *msg,
+					    const struct dpll_pin_attr *attr)
+{
+	enum dpll_pin_type i;
+	int ret;
+
+	for (i = DPLL_PIN_TYPE_UNSPEC + 1; i <= DPLL_PIN_TYPE_MAX; i++) {
+		if (dpll_pin_attr_type_supported(attr, i)) {
+			ret = __dpll_msg_add_pin_type(msg,
+						      DPLLA_PIN_TYPE_SUPPORTED,
+						      i);
+			if (ret)
+				return ret;
+		}
+	}
+
+	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_pin_attr *attr)
+{
+	enum dpll_pin_signal_type t = dpll_pin_attr_signal_type_get(attr);
+
+	if (t == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
+		return 0;
+
+	return __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
+}
+
+static int
+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
+					const struct dpll_pin_attr *attr)
+{
+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
+	enum dpll_pin_signal_type i;
+	int ret;
+
+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
+		if (dpll_pin_attr_signal_type_supported(attr, i)) {
+			ret = __dpll_msg_add_pin_signal_type(msg, da, i);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_custom_freq(struct sk_buff *msg,
+					const struct dpll_pin_attr *attr)
+{
+	u32 freq;
+
+	if (dpll_pin_attr_custom_freq_get(attr, &freq))
+		return 0;
+	if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_states(struct sk_buff *msg,
+				   const struct dpll_pin_attr *attr)
+{
+	enum dpll_pin_state i;
+
+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
+		if (dpll_pin_attr_state_enabled(attr, i))
+			if (nla_put_s32(msg, DPLLA_PIN_STATE, i))
+				return -EMSGSIZE;
+
+	return 0;
+}
+
+static int dpll_msg_add_pin_states_supported(struct sk_buff *msg,
+					     const struct dpll_pin_attr *attr)
+{
+	enum dpll_pin_state i;
+
+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
+		if (dpll_pin_attr_state_supported(attr, i))
+			if (nla_put_s32(msg, DPLLA_PIN_STATE_SUPPORTED, i))
+				return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_pin_attr *attr)
+{
+	u32 prio;
+
+	if (dpll_pin_attr_prio_get(attr, &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_pin_attr *attr)
+{
+	unsigned int netifindex; // TODO: Should be u32?
+
+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
+		return 0;
+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int
+dpll_msg_add_event_change_type(struct sk_buff *msg,
+			       enum dpll_event_change event)
+{
+	if (nla_put_s32(msg, DPLLA_CHANGE_TYPE, event))
+		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_attr *attr = dpll_pin_attr_alloc();
+	struct dpll_pin *parent = NULL;
+	int ret;
+
+	if (!attr)
+		return -ENOMEM;
+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_description(msg, dpll_pin_get_description(pin));
+	if (ret)
+		goto out;
+	parent = dpll_pin_get_parent(pin);
+	if (parent) {
+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
+								    parent));
+		if (ret)
+			goto out;
+	}
+	ret = dpll_pin_get_attr(dpll, pin, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_type(msg, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_types_supported(msg, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_signal_type(msg, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_signal_types_supported(msg, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_custom_freq(msg, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_states(msg, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_states_supported(msg, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_prio(msg, attr);
+	if (ret)
+		goto out;
+	ret = dpll_msg_add_pin_netifindex(msg, attr);
+	if (ret)
+		goto out;
+out:
+	dpll_pin_attr_free(attr);
+
+	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)
+{
+	struct dpll_attr *attr = dpll_attr_alloc();
+	int ret = dpll_get_attr(dpll, attr);
+
+	if (ret)
+		return -EAGAIN;
+	if (dpll_msg_add_source_pin(msg, attr))
+		return -EMSGSIZE;
+	if (dpll_msg_add_temp(msg, attr))
+		return -EMSGSIZE;
+	if (dpll_msg_add_lock_status(msg, attr))
+		return -EMSGSIZE;
+	if (dpll_msg_add_mode(msg, attr))
+		return -EMSGSIZE;
+	if (dpll_msg_add_modes_supported(msg, attr))
+		return -EMSGSIZE;
+	if (dpll_msg_add_netifindex(msg, attr))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+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_DUMP_FILTER_STATUS) {
+		ret = __dpll_cmd_dump_status(msg, dpll);
+		if (ret)
+			goto out_unlock;
+	}
+	if (dump_filter & DPLL_DUMP_FILTER_PINS)
+		ret = __dpll_cmd_dump_pins(msg, dpll);
+	dpll_unlock(dpll);
+
+	return ret;
+out_unlock:
+	dpll_unlock(dpll);
+	return ret;
+}
+
+static enum dpll_pin_type dpll_msg_read_pin_type(struct nlattr *a)
+{
+	return nla_get_s32(a);
+}
+
+static enum dpll_pin_signal_type dpll_msg_read_pin_sig_type(struct nlattr *a)
+{
+	return nla_get_s32(a);
+}
+
+static u32 dpll_msg_read_pin_custom_freq(struct nlattr *a)
+{
+	return nla_get_u32(a);
+}
+
+static enum dpll_pin_state dpll_msg_read_pin_state(struct nlattr *a)
+{
+	return nla_get_s32(a);
+}
+
+static u32 dpll_msg_read_pin_prio(struct nlattr *a)
+{
+	return nla_get_u32(a);
+}
+
+static u32 dpll_msg_read_dump_filter(struct nlattr *a)
+{
+	return nla_get_u32(a);
+}
+
+static int
+dpll_pin_attr_from_nlattr(struct dpll_pin_attr *pa, struct genl_info *info)
+{
+	enum dpll_pin_signal_type st;
+	enum dpll_pin_state state;
+	enum dpll_pin_type t;
+	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_TYPE:
+			t = dpll_msg_read_pin_type(a);
+			ret = dpll_pin_attr_type_set(pa, t);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_SIGNAL_TYPE:
+			st = dpll_msg_read_pin_sig_type(a);
+			ret = dpll_pin_attr_signal_type_set(pa, st);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_CUSTOM_FREQ:
+			freq = dpll_msg_read_pin_custom_freq(a);
+			ret = dpll_pin_attr_custom_freq_set(pa, freq);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_STATE:
+			state = dpll_msg_read_pin_state(a);
+			ret = dpll_pin_attr_state_set(pa, state);
+			if (ret)
+				return ret;
+			break;
+		case DPLLA_PIN_PRIO:
+			prio = dpll_msg_read_pin_prio(a);
+			ret = dpll_pin_attr_prio_set(pa, 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_pin_attr *old = NULL, *new = NULL, *delta = NULL;
+	struct dpll_device *dpll = info->user_ptr[0];
+	struct nlattr **attrs = info->attrs;
+	struct dpll_pin *pin;
+	int ret, pin_id;
+
+	if (!attrs[DPLLA_PIN_IDX])
+		return -EINVAL;
+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
+	old = dpll_pin_attr_alloc();
+	new = dpll_pin_attr_alloc();
+	delta = dpll_pin_attr_alloc();
+	if (!old || !new || !delta) {
+		ret = -ENOMEM;
+		goto mem_free;
+	}
+	dpll_lock(dpll);
+	pin = dpll_pin_get_by_idx(dpll, pin_id);
+	if (!pin) {
+		ret = -ENODEV;
+		goto mem_free_unlock;
+	}
+	ret = dpll_pin_get_attr(dpll, pin, old);
+	if (ret)
+		goto mem_free_unlock;
+	ret = dpll_pin_attr_from_nlattr(new, info);
+	if (ret)
+		goto mem_free_unlock;
+	ret = dpll_pin_attr_delta(delta, new, old);
+	dpll_unlock(dpll);
+	if (!ret)
+		ret = dpll_pin_set_attr(dpll, pin, delta);
+	else
+		ret = -EINVAL;
+
+	dpll_pin_attr_free(delta);
+	dpll_pin_attr_free(new);
+	dpll_pin_attr_free(old);
+
+	return ret;
+
+mem_free_unlock:
+	dpll_unlock(dpll);
+mem_free:
+	dpll_pin_attr_free(delta);
+	dpll_pin_attr_free(new);
+	dpll_pin_attr_free(old);
+	return ret;
+}
+
+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_attr_from_nlattr(struct dpll_attr *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_attr_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_attr_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_attr *old = NULL, *new = NULL, *delta = NULL;
+	struct dpll_device *dpll = info->user_ptr[0];
+	int ret;
+
+	old = dpll_attr_alloc();
+	new = dpll_attr_alloc();
+	delta = dpll_attr_alloc();
+	if (!old || !new || !delta) {
+		ret = -ENOMEM;
+		goto mem_free;
+	}
+	dpll_lock(dpll);
+	ret = dpll_get_attr(dpll, old);
+	dpll_unlock(dpll);
+	if (!ret) {
+		dpll_attr_from_nlattr(new, info);
+		ret = dpll_attr_delta(delta, new, old);
+		if (!ret)
+			ret = dpll_set_attr(dpll, delta);
+	}
+
+mem_free:
+	dpll_attr_free(old);
+	dpll_attr_free(new);
+	dpll_attr_free(delta);
+
+	return ret;
+}
+
+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_DUMP_FILTER])
+		dump_filter =
+			dpll_msg_read_dump_filter(attrs[DPLLA_DUMP_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_DUMP_FILTER];
+
+	if (attr)
+		ctx->dump_filter = dpll_msg_read_dump_filter(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 dpll_param *p)
+{
+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
+
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_name(p->msg, dpll_dev_name(p->dpll));
+
+	return ret;
+}
+
+static int dpll_event_device_change(struct dpll_param *p)
+{
+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
+
+	if (ret)
+		return ret;
+	ret = dpll_msg_add_event_change_type(p->msg, p->change_type);
+	if (ret)
+		return ret;
+	switch (p->change_type)	{
+	case DPLL_CHANGE_PIN_ADD:
+	case DPLL_CHANGE_PIN_DEL:
+	case DPLL_CHANGE_PIN_TYPE:
+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
+	case DPLL_CHANGE_PIN_STATE:
+	case DPLL_CHANGE_PIN_PRIO:
+		ret = dpll_msg_add_pin_idx(p->msg, dpll_pin_idx(p->dpll, p->pin));
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static const cb_t event_cb[] = {
+	[DPLL_EVENT_DEVICE_CREATE]	= dpll_event_device_id,
+	[DPLL_EVENT_DEVICE_DELETE]	= dpll_event_device_id,
+	[DPLL_EVENT_DEVICE_CHANGE]	= dpll_event_device_change,
+};
+
+/*
+ * Generic netlink DPLL event encoding
+ */
+static int dpll_send_event(enum dpll_event event, struct dpll_param *p)
+{
+	struct sk_buff *msg;
+	int ret = -EMSGSIZE;
+	void *hdr;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+	p->msg = msg;
+
+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
+	if (!hdr)
+		goto out_free_msg;
+
+	ret = event_cb[event](p);
+	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)
+{
+	struct dpll_param p = { .dpll = dpll };
+
+	return dpll_send_event(DPLL_EVENT_DEVICE_CREATE, &p);
+}
+
+int dpll_notify_device_delete(struct dpll_device *dpll)
+{
+	struct dpll_param p = { .dpll = dpll };
+
+	return dpll_send_event(DPLL_EVENT_DEVICE_DELETE, &p);
+}
+
+int dpll_notify_device_change(struct dpll_device *dpll,
+			      enum dpll_event_change event,
+			      struct dpll_pin *pin)
+{
+	struct dpll_param p = { .dpll = dpll,
+				.change_type = event,
+				.pin = pin };
+
+	return dpll_send_event(DPLL_EVENT_DEVICE_CHANGE, &p);
+}
+EXPORT_SYMBOL_GPL(dpll_notify_device_change);
+
+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..a9cbf260624d
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,261 @@
+/* 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/dpll_attr.h>
+#include <linux/device.h>
+
+struct dpll_device;
+struct dpll_pin;
+
+#define DPLL_COOKIE_LEN		10
+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
+struct dpll_device_ops {
+	int (*get)(struct dpll_device *dpll, struct dpll_attr *attr);
+	int (*set)(struct dpll_device *dpll, const struct dpll_attr *attr);
+};
+
+struct dpll_pin_ops {
+	int (*get)(struct dpll_device *dpll, struct dpll_pin *pin,
+		   struct dpll_pin_attr *attr);
+	int (*set)(struct dpll_device *dpll, struct dpll_pin *pin,
+		   const struct dpll_pin_attr *attr);
+	int (*select)(struct dpll_device *dpll, struct dpll_pin *pin);
+};
+
+enum dpll_type {
+	DPLL_TYPE_UNSPEC,
+	DPLL_TYPE_PPS,
+	DPLL_TYPE_EEC,
+
+	__DPLL_TYPE_MAX
+};
+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
+
+/**
+ * dpll_device_alloc - allocate memory for a new dpll_device object
+ * @ops: pointer to dpll operations structure
+ * @type: type of a dpll being allocated
+ * @cookie: a system unique number for a device
+ * @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: cookie, type and dev_driver_idx.
+ * Finding allocated and registered dpll device is also possible with
+ * the: cookie, 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 u8 cookie[DPLL_COOKIE_LEN], u8 dev_driver_idx,
+		   void *priv, struct device *parent);
+
+/**
+ * dpll_device_register - registers allocated dpll
+ * @dpll: pointer to dpll
+ *
+ * Register the dpll on the dpll subsystem, make it available for netlink
+ * API users.
+ */
+void dpll_device_register(struct dpll_device *dpll);
+
+/**
+ * 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(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(struct dpll_device *dpll, 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
+ * @pin_idx: index of a pin on dpll device (@dpll_pin_owner)
+ *	     that is being registered on 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, u32 pin_idx,
+			 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
+ * @desc_len: number of chars in description
+ *
+ * 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, size_t desc_len);
+
+
+/**
+ * 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: pointer to object to register pin with
+ * @pin: pointer to allocated pin object being deregistered from dpll
+ * @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,
+			    struct dpll_pin *parent_pin, struct dpll_pin *pin,
+			    struct dpll_pin_ops *ops, void *priv);
+/**
+ * dpll_device_get_by_cookie - find a dpll by its cookie
+ * @cookie: cookie of dpll to search for, 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_cookie(u8 cookie[DPLL_COOKIE_LEN],
+					      enum dpll_type type, u8 idx);
+
+/**
+ * dpll_pin_get_by_description - find a pin by its description
+ * @dpll: dpll device pointer
+ * @description: string description 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_description(struct dpll_device *dpll,
+					     const char *description);
+
+/**
+ * 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);
+
+int dpll_notify_device_change(struct dpll_device *dpll,
+			      enum dpll_event_change event,
+			      struct dpll_pin *pin);
+#endif
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
new file mode 100644
index 000000000000..16278618d59d
--- /dev/null
+++ b/include/uapi/linux/dpll.h
@@ -0,0 +1,263 @@
+/* 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 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_DUMP_FILTER_PINS	1
+#define DPLL_DUMP_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_DUMP_FILTER - filter bitmask (int, sum of DPLL_DUMP_FILTER_* defines)
+ * @DPLLA_NETIFINDEX - related network interface index
+ * @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_TYPE_SUPPORTED - pin types supported (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_STATE - state of pin's capabilities (enum dpll_pin_state)
+ * @DPLLA_PIN_STATE_SUPPORTED - available pin's capabilities
+ *	(enum dpll_pin_state)
+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
+ * @DPLLA_CHANGE_TYPE - type of device change event
+ *	(enum dpll_change_type)
+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
+ **/
+enum dplla {
+	DPLLA_UNSPEC,
+	DPLLA_ID,
+	DPLLA_NAME,
+	DPLLA_MODE,
+	DPLLA_MODE_SUPPORTED,
+	DPLLA_SOURCE_PIN_IDX,
+	DPLLA_LOCK_STATUS,
+	DPLLA_TEMP,
+	DPLLA_DUMP_FILTER,
+	DPLLA_NETIFINDEX,
+	DPLLA_PIN,
+	DPLLA_PIN_IDX,
+	DPLLA_PIN_DESCRIPTION,
+	DPLLA_PIN_TYPE,
+	DPLLA_PIN_TYPE_SUPPORTED,
+	DPLLA_PIN_SIGNAL_TYPE,
+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
+	DPLLA_PIN_CUSTOM_FREQ,
+	DPLLA_PIN_STATE,
+	DPLLA_PIN_STATE_SUPPORTED,
+	DPLLA_PIN_PRIO,
+	DPLLA_PIN_PARENT_IDX,
+	DPLLA_CHANGE_TYPE,
+	DPLLA_PIN_NETIFINDEX,
+	__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_state - available pin states
+ *
+ * @DPLL_PIN_STATE_UNSPEC - unspecified value
+ * @DPLL_PIN_STATE_CONNECTED - pin connected
+ * @DPLL_PIN_STATE_DISCONNECTED - pin disconnected
+ * @DPLL_PIN_STATE_SOURCE - pin used as an input pin
+ * @DPLL_PIN_STATE_OUTPUT - pin used as an output pin
+ **/
+enum dpll_pin_state {
+	DPLL_PIN_STATE_UNSPEC,
+	DPLL_PIN_STATE_CONNECTED,
+	DPLL_PIN_STATE_DISCONNECTED,
+	DPLL_PIN_STATE_SOURCE,
+	DPLL_PIN_STATE_OUTPUT,
+
+	__DPLL_PIN_STATE_MAX,
+};
+
+#define DPLL_PIN_STATE_MAX (__DPLL_PIN_STATE_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_TYPE - pin type cahnged,
+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
+ * @DPLL_CHANGE_PIN_STATE - 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_TYPE,
+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
+	DPLL_CHANGE_PIN_STATE,
+	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_FORCED - 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_FORCED,
+	DPLL_MODE_AUTOMATIC,
+	DPLL_MODE_HOLDOVER,
+	DPLL_MODE_FREERUN,
+	DPLL_MODE_NCO,
+
+	__DPLL_MODE_MAX,
+};
+
+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
+
+#endif /* _UAPI_LINUX_DPLL_H */
-- 
2.27.0


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

* [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
  2022-11-29 21:37 [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Vadim Fedorenko
  2022-11-29 21:37 ` [RFC PATCH v4 1/4] dpll: add dpll_attr/dpll_pin_attr helper classes Vadim Fedorenko
  2022-11-29 21:37 ` [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions Vadim Fedorenko
@ 2022-11-29 21:37 ` Vadim Fedorenko
  2022-12-19  9:13   ` Paolo Abeni
  2022-11-29 21:37 ` [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops Vadim Fedorenko
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 87+ messages in thread
From: Vadim Fedorenko @ 2022-11-29 21:37 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

From: Vadim Fedorenko <vadfed@fb.com>

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@fb.com>
---
 Documentation/networking/dpll.rst  | 271 +++++++++++++++++++++++++++++
 Documentation/networking/index.rst |   1 +
 2 files changed, 272 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..58401e2b70a7
--- /dev/null
+++ b/Documentation/networking/dpll.rst
@@ -0,0 +1,271 @@
+.. 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_SGINAL_TYPE, DPLL_PIN_TYPE,
+DPLL_PIN_STATE), 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.
+
+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
+    ``NETIFINDEX``              attr dpll owner Linux netdevice index
+  ``DEVICE_SET``                userspace to set dpll device
+                                configuration
+    ``ID``                      attr internal dpll device index
+    ``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
+    ``PIN_IDX``                 attr index of a pin to configure
+    ``PIN_TYPE``                attr type configuration value for
+                                selected pin
+    ``PIN_SIGNAL_TYPE``         attr signal type configuration value
+                                for selected pin
+    ``PIN_CUSTOM_FREQ``         attr signal custom frequency to be set
+    ``PIN_STATE``               attr pin state to be set
+    ``PIN_PRIO``                attr pin priority to be set
+
+Netlink dump requests
+=====================
+The ``DEVICE_GET`` command is capable of dump type netlink requests.
+In such case the userspace shall provide ``DUMP_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 ``DPLLA_DUMP_FILTER`` attribute:
+
+  =============================== ====================================
+  ``DPLL_DUMP_FILTER_PINS``       value of ``DUMP_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_TYPE_SUPPORTED``        attr value of supported 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_STATE``                 attr value of pin state
+    ``PIN_STATE_SUPPORTED``       attr value of supported pin state
+    ``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_DUMP_FILTER_STATUS``     value of ``DUMP_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
+    ``NETIFINDEX``                attr dpll owner Linux netdevice index
+
+
+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_STATE`` and ``PIN_STATE_SUPPORTED`` attributes:
+
+============================= ============================
+  ``PIN_STATE_CONNECTED``     Pin connected to a dpll
+  ``PIN_STATE_DISCONNECTED``  Pin disconnected from dpll
+  ``PIN_STATE_SOURCE``        Source pin
+  ``PIN_STATE_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 ise 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_TYPE``        pin type has chaned
+   ``CHANGE_PIN_SIGNAL_TYPE`` pin signal type has changed
+   ``CHANGE_PIN_CUSTOM_FREQ`` pin custom frequency value has changed
+   ``CHANGE_PIN_STATE``       pin state 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`` and
+``dpll_device_register`` provide the operations set, unique device
+cookie, type of dpll (PPS/EEC), and pointers to parent device and
+its private data for calling back the ops.
+
+The pins are allocated separately with ``dpll_pin_alloc``, which
+requires providing pin description and its length.
+
+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_cookie`` providing the same cookie, 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-%s-%s%d`` witch arguments:
+``dev_driver_string(parent)``        - syscall on parent device
+``dev_name(parent)``                 - syscall on parent device
+``type ? dpll_type_str(type) : ""``  - DPLL type converted to string
+``idx``                              - registerer given index
+
+Notifications of adding or removing DPLL devices are created within
+subsystem itself.
+Notifications about configurations changes are also invoked when
+requested change was successfully accepted by device driver with
+corresponding set command.
+Although the interface provides device drivers with
+``dpll_notify_device_change``, so notifications or alarms can be
+requested by device driver if needed, as different ways of confirmation
+could be used. All the interfaces for notification messages could be
+found 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.27.0


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

* [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-11-29 21:37 [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Vadim Fedorenko
                   ` (2 preceding siblings ...)
  2022-11-29 21:37 ` [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface Vadim Fedorenko
@ 2022-11-29 21:37 ` Vadim Fedorenko
  2022-11-30 12:41   ` Jiri Pirko
  2022-11-30 12:32 ` [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Jiri Pirko
  2023-01-12 12:23 ` Kubalewski, Arkadiusz
  5 siblings, 1 reply; 87+ messages in thread
From: Vadim Fedorenko @ 2022-11-29 21:37 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni
  Cc: netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

From: Vadim Fedorenko <vadfed@fb.com>

Implement basic DPLL operations in ptp_ocp driver as the
simplest example of using new subsystem.

Signed-off-by: Vadim Fedorenko <vadfed@fb.com>
---
 drivers/ptp/Kconfig   |   1 +
 drivers/ptp/ptp_ocp.c | 123 +++++++++++++++++++++++++++++-------------
 2 files changed, 87 insertions(+), 37 deletions(-)

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index fe4971b65c64..8c4cfabc1bfa 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -177,6 +177,7 @@ config PTP_1588_CLOCK_OCP
 	depends on COMMON_CLK
 	select NET_DEVLINK
 	select CRC16
+	select DPLL
 	help
 	  This driver adds support for an OpenCompute time card.
 
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 154d58cbd9ce..605853ac4a12 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -23,6 +23,8 @@
 #include <linux/mtd/mtd.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
+#include <linux/dpll.h>
+#include <uapi/linux/dpll.h>
 
 #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
 #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
@@ -353,6 +355,7 @@ struct ptp_ocp {
 	struct ptp_ocp_signal	signal[4];
 	struct ptp_ocp_sma_connector sma[4];
 	const struct ocp_sma_op *sma_op;
+	struct dpll_device *dpll;
 };
 
 #define OCP_REQ_TIMESTAMP	BIT(0)
@@ -835,18 +838,19 @@ static DEFINE_IDR(ptp_ocp_idr);
 struct ocp_selector {
 	const char *name;
 	int value;
+	int dpll_type;
 };
 
 static const struct ocp_selector ptp_ocp_clock[] = {
-	{ .name = "NONE",	.value = 0 },
-	{ .name = "TOD",	.value = 1 },
-	{ .name = "IRIG",	.value = 2 },
-	{ .name = "PPS",	.value = 3 },
-	{ .name = "PTP",	.value = 4 },
-	{ .name = "RTC",	.value = 5 },
-	{ .name = "DCF",	.value = 6 },
-	{ .name = "REGS",	.value = 0xfe },
-	{ .name = "EXT",	.value = 0xff },
+	{ .name = "NONE",	.value = 0,		.dpll_type = 0 },
+	{ .name = "TOD",	.value = 1,		.dpll_type = 0 },
+	{ .name = "IRIG",	.value = 2,		.dpll_type = 0 },
+	{ .name = "PPS",	.value = 3,		.dpll_type = 0 },
+	{ .name = "PTP",	.value = 4,		.dpll_type = 0 },
+	{ .name = "RTC",	.value = 5,		.dpll_type = 0 },
+	{ .name = "DCF",	.value = 6,		.dpll_type = 0 },
+	{ .name = "REGS",	.value = 0xfe,		.dpll_type = 0 },
+	{ .name = "EXT",	.value = 0xff,		.dpll_type = 0 },
 	{ }
 };
 
@@ -855,37 +859,37 @@ static const struct ocp_selector ptp_ocp_clock[] = {
 #define SMA_SELECT_MASK		GENMASK(14, 0)
 
 static const struct ocp_selector ptp_ocp_sma_in[] = {
-	{ .name = "10Mhz",	.value = 0x0000 },
-	{ .name = "PPS1",	.value = 0x0001 },
-	{ .name = "PPS2",	.value = 0x0002 },
-	{ .name = "TS1",	.value = 0x0004 },
-	{ .name = "TS2",	.value = 0x0008 },
-	{ .name = "IRIG",	.value = 0x0010 },
-	{ .name = "DCF",	.value = 0x0020 },
-	{ .name = "TS3",	.value = 0x0040 },
-	{ .name = "TS4",	.value = 0x0080 },
-	{ .name = "FREQ1",	.value = 0x0100 },
-	{ .name = "FREQ2",	.value = 0x0200 },
-	{ .name = "FREQ3",	.value = 0x0400 },
-	{ .name = "FREQ4",	.value = 0x0800 },
-	{ .name = "None",	.value = SMA_DISABLE },
+	{ .name = "10Mhz",	.value = 0x0000,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_10_MHZ },
+	{ .name = "PPS1",	.value = 0x0001,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_1_PPS },
+	{ .name = "PPS2",	.value = 0x0002,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_1_PPS },
+	{ .name = "TS1",	.value = 0x0004,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "TS2",	.value = 0x0008,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "IRIG",	.value = 0x0010,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "DCF",	.value = 0x0020,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "TS3",	.value = 0x0040,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "TS4",	.value = 0x0080,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "FREQ1",	.value = 0x0100,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "FREQ2",	.value = 0x0200,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "FREQ3",	.value = 0x0400,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "FREQ4",	.value = 0x0800,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "None",	.value = SMA_DISABLE,	.dpll_type = 0 },
 	{ }
 };
 
 static const struct ocp_selector ptp_ocp_sma_out[] = {
-	{ .name = "10Mhz",	.value = 0x0000 },
-	{ .name = "PHC",	.value = 0x0001 },
-	{ .name = "MAC",	.value = 0x0002 },
-	{ .name = "GNSS1",	.value = 0x0004 },
-	{ .name = "GNSS2",	.value = 0x0008 },
-	{ .name = "IRIG",	.value = 0x0010 },
-	{ .name = "DCF",	.value = 0x0020 },
-	{ .name = "GEN1",	.value = 0x0040 },
-	{ .name = "GEN2",	.value = 0x0080 },
-	{ .name = "GEN3",	.value = 0x0100 },
-	{ .name = "GEN4",	.value = 0x0200 },
-	{ .name = "GND",	.value = 0x2000 },
-	{ .name = "VCC",	.value = 0x4000 },
+	{ .name = "10Mhz",	.value = 0x0000,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_10_MHZ },
+	{ .name = "PHC",	.value = 0x0001,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "MAC",	.value = 0x0002,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "GNSS1",	.value = 0x0004,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_1_PPS },
+	{ .name = "GNSS2",	.value = 0x0008,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_1_PPS },
+	{ .name = "IRIG",	.value = 0x0010,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "DCF",	.value = 0x0020,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "GEN1",	.value = 0x0040,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "GEN2",	.value = 0x0080,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "GEN3",	.value = 0x0100,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "GEN4",	.value = 0x0200,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
+	{ .name = "GND",	.value = 0x2000,	.dpll_type = 0 },
+	{ .name = "VCC",	.value = 0x4000,	.dpll_type = 0 },
 	{ }
 };
 
@@ -4175,12 +4179,41 @@ ptp_ocp_detach(struct ptp_ocp *bp)
 	device_unregister(&bp->dev);
 }
 
+static int ptp_ocp_dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr)
+{
+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
+	int sync;
+
+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
+	dpll_attr_lock_status_set(attr, sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED);
+
+	return 0;
+}
+
+static int ptp_ocp_dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
+				     struct dpll_pin_attr *attr)
+{
+	dpll_pin_attr_type_set(attr, DPLL_PIN_TYPE_EXT);
+	return 0;
+}
+
+static struct dpll_device_ops dpll_ops = {
+	.get	= ptp_ocp_dpll_get_attr,
+};
+
+static struct dpll_pin_ops dpll_pin_ops = {
+	.get	= ptp_ocp_dpll_pin_get_attr,
+};
+
 static int
 ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
+	const u8 dpll_cookie[DPLL_COOKIE_LEN] = { "OCP" };
+	char pin_desc[PIN_DESC_LEN];
 	struct devlink *devlink;
+	struct dpll_pin *pin;
 	struct ptp_ocp *bp;
-	int err;
+	int err, i;
 
 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
 	if (!devlink) {
@@ -4230,6 +4263,20 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	ptp_ocp_info(bp);
 	devlink_register(devlink);
+
+	bp->dpll = dpll_device_alloc(&dpll_ops, DPLL_TYPE_PPS, dpll_cookie, pdev->bus->number, bp, &pdev->dev);
+	if (!bp->dpll) {
+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
+		goto out;
+	}
+	dpll_device_register(bp->dpll);
+
+	for (i = 0; i < 4; i++) {
+		snprintf(pin_desc, PIN_DESC_LEN, "sma%d", i + 1);
+		pin = dpll_pin_alloc(pin_desc, PIN_DESC_LEN);
+		dpll_pin_register(bp->dpll, pin, &dpll_pin_ops, bp);
+	}
+
 	return 0;
 
 out:
@@ -4247,6 +4294,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
 	struct devlink *devlink = priv_to_devlink(bp);
 
+	dpll_device_unregister(bp->dpll);
+	dpll_device_free(bp->dpll);
 	devlink_unregister(devlink);
 	ptp_ocp_detach(bp);
 	pci_disable_device(pdev);
-- 
2.27.0


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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-11-29 21:37 [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Vadim Fedorenko
                   ` (3 preceding siblings ...)
  2022-11-29 21:37 ` [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops Vadim Fedorenko
@ 2022-11-30 12:32 ` Jiri Pirko
  2022-12-02 11:27   ` Kubalewski, Arkadiusz
  2023-01-12 12:23 ` Kubalewski, Arkadiusz
  5 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-11-30 12:32 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk

Tue, Nov 29, 2022 at 10:37:20PM CET, vfedorenko@novek.ru wrote:
>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.

Overall, I see a lot of issues on multiple levels. I will go over them
in follow-up emails. So far, after couple of hours looking trought this,
I have following general feelings/notes:

1) Netlink interface looks much saner than in previous versions. I will
   send couple of notes, mainly around events and object mixtures and
   couple of bugs/redundancies. But overall, looks fineish.

2) I don't like that concept of a shared pin, at all. It makes things
   unnecessary complicated. Just have a pin created for dpll instance
   and that's it. If another instance has the same pin, it should create
   it as well. Keeps things separate and easy to model. Let the
   hw/fw/driver figure out the implementation oddities.
   Why exactly you keep pushing the shared pin idea? Perhaps I'm missing
   something crucial.

3) I don't like the concept of muxed pins and hierarchies of pins. Why
   does user care? If pin is muxed, the rest of the pins related to this
   one should be in state disabled/disconnected. The user only cares
   about to see which pins are related to each other. It can be easily
   exposed by "muxid" like this:
   pin 1
   pin 2
   pin 3 muxid 100
   pin 4 muxid 100
   pin 5 muxid 101
   pin 6 muxid 101
   In this example pins 3,4 and 5,6 are muxed, therefore the user knows
   if he connects one, the other one gets disconnected (or will have to
   disconnect the first one explicitly first).

4) I don't like the "attr" indirection. It makes things very tangled. It
   comes from the concepts of classes and objects and takes it to
   extreme. Not really something we are commonly used to in kernel.
   Also, it brings no value from what I can see, only makes things very
   hard to read and follow.

   Please keep things direct and simple:
   * If some option could be changed for a pin or dpll, just have an
     op that is directly called from netlink handler to change it.
     There should be clear set of ops for configuration of pin and
     dpll object. This "attr" indirection make this totally invisible.
   * If some attribute is const during dpll or pin lifetime, have it
     passed during dpll or pin creation.



>
>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 (1):
>  dpll: add dpll_attr/dpll_pin_attr helper classes
>
>Vadim Fedorenko (3):
>  dpll: Add DPLL framework base functions
>  dpll: documentation on DPLL subsystem interface
>  ptp_ocp: implement DPLL ops
>
> Documentation/networking/dpll.rst  | 271 ++++++++
> Documentation/networking/index.rst |   1 +
> MAINTAINERS                        |   8 +
> drivers/Kconfig                    |   2 +
> drivers/Makefile                   |   1 +
> drivers/dpll/Kconfig               |   7 +
> drivers/dpll/Makefile              |  11 +
> drivers/dpll/dpll_attr.c           | 278 +++++++++
> drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
> drivers/dpll/dpll_core.h           | 176 ++++++
> drivers/dpll/dpll_netlink.c        | 963 +++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h        |  24 +
> drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
> drivers/ptp/Kconfig                |   1 +
> drivers/ptp/ptp_ocp.c              | 123 ++--
> include/linux/dpll.h               | 261 ++++++++
> include/linux/dpll_attr.h          | 433 +++++++++++++
> include/uapi/linux/dpll.h          | 263 ++++++++
> 18 files changed, 4002 insertions(+), 37 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_attr.c
> 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/dpll/dpll_pin_attr.c
> create mode 100644 include/linux/dpll.h
> create mode 100644 include/linux/dpll_attr.h
> create mode 100644 include/uapi/linux/dpll.h
>
>-- 
>2.27.0
>

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-11-29 21:37 ` [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops Vadim Fedorenko
@ 2022-11-30 12:41   ` Jiri Pirko
  2022-12-02 11:27     ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-11-30 12:41 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk

Tue, Nov 29, 2022 at 10:37:24PM CET, vfedorenko@novek.ru wrote:
>From: Vadim Fedorenko <vadfed@fb.com>
>
>Implement basic DPLL operations in ptp_ocp driver as the
>simplest example of using new subsystem.
>
>Signed-off-by: Vadim Fedorenko <vadfed@fb.com>
>---
> drivers/ptp/Kconfig   |   1 +
> drivers/ptp/ptp_ocp.c | 123 +++++++++++++++++++++++++++++-------------
> 2 files changed, 87 insertions(+), 37 deletions(-)
>
>diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>index fe4971b65c64..8c4cfabc1bfa 100644
>--- a/drivers/ptp/Kconfig
>+++ b/drivers/ptp/Kconfig
>@@ -177,6 +177,7 @@ config PTP_1588_CLOCK_OCP
> 	depends on COMMON_CLK
> 	select NET_DEVLINK
> 	select CRC16
>+	select DPLL
> 	help
> 	  This driver adds support for an OpenCompute time card.
> 
>diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
>index 154d58cbd9ce..605853ac4a12 100644
>--- a/drivers/ptp/ptp_ocp.c
>+++ b/drivers/ptp/ptp_ocp.c
>@@ -23,6 +23,8 @@
> #include <linux/mtd/mtd.h>
> #include <linux/nvmem-consumer.h>
> #include <linux/crc16.h>
>+#include <linux/dpll.h>
>+#include <uapi/linux/dpll.h>
> 
> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>@@ -353,6 +355,7 @@ struct ptp_ocp {
> 	struct ptp_ocp_signal	signal[4];
> 	struct ptp_ocp_sma_connector sma[4];
> 	const struct ocp_sma_op *sma_op;
>+	struct dpll_device *dpll;
> };
> 
> #define OCP_REQ_TIMESTAMP	BIT(0)
>@@ -835,18 +838,19 @@ static DEFINE_IDR(ptp_ocp_idr);
> struct ocp_selector {
> 	const char *name;
> 	int value;
>+	int dpll_type;
> };
> 
> static const struct ocp_selector ptp_ocp_clock[] = {
>-	{ .name = "NONE",	.value = 0 },
>-	{ .name = "TOD",	.value = 1 },
>-	{ .name = "IRIG",	.value = 2 },
>-	{ .name = "PPS",	.value = 3 },
>-	{ .name = "PTP",	.value = 4 },
>-	{ .name = "RTC",	.value = 5 },
>-	{ .name = "DCF",	.value = 6 },
>-	{ .name = "REGS",	.value = 0xfe },
>-	{ .name = "EXT",	.value = 0xff },
>+	{ .name = "NONE",	.value = 0,		.dpll_type = 0 },
>+	{ .name = "TOD",	.value = 1,		.dpll_type = 0 },
>+	{ .name = "IRIG",	.value = 2,		.dpll_type = 0 },
>+	{ .name = "PPS",	.value = 3,		.dpll_type = 0 },
>+	{ .name = "PTP",	.value = 4,		.dpll_type = 0 },
>+	{ .name = "RTC",	.value = 5,		.dpll_type = 0 },
>+	{ .name = "DCF",	.value = 6,		.dpll_type = 0 },
>+	{ .name = "REGS",	.value = 0xfe,		.dpll_type = 0 },
>+	{ .name = "EXT",	.value = 0xff,		.dpll_type = 0 },
> 	{ }
> };
> 
>@@ -855,37 +859,37 @@ static const struct ocp_selector ptp_ocp_clock[] = {
> #define SMA_SELECT_MASK		GENMASK(14, 0)
> 
> static const struct ocp_selector ptp_ocp_sma_in[] = {
>-	{ .name = "10Mhz",	.value = 0x0000 },
>-	{ .name = "PPS1",	.value = 0x0001 },
>-	{ .name = "PPS2",	.value = 0x0002 },
>-	{ .name = "TS1",	.value = 0x0004 },
>-	{ .name = "TS2",	.value = 0x0008 },
>-	{ .name = "IRIG",	.value = 0x0010 },
>-	{ .name = "DCF",	.value = 0x0020 },
>-	{ .name = "TS3",	.value = 0x0040 },
>-	{ .name = "TS4",	.value = 0x0080 },
>-	{ .name = "FREQ1",	.value = 0x0100 },
>-	{ .name = "FREQ2",	.value = 0x0200 },
>-	{ .name = "FREQ3",	.value = 0x0400 },
>-	{ .name = "FREQ4",	.value = 0x0800 },
>-	{ .name = "None",	.value = SMA_DISABLE },
>+	{ .name = "10Mhz",	.value = 0x0000,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_10_MHZ },
>+	{ .name = "PPS1",	.value = 0x0001,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_1_PPS },
>+	{ .name = "PPS2",	.value = 0x0002,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_1_PPS },
>+	{ .name = "TS1",	.value = 0x0004,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "TS2",	.value = 0x0008,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "IRIG",	.value = 0x0010,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "DCF",	.value = 0x0020,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "TS3",	.value = 0x0040,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "TS4",	.value = 0x0080,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "FREQ1",	.value = 0x0100,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "FREQ2",	.value = 0x0200,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "FREQ3",	.value = 0x0400,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "FREQ4",	.value = 0x0800,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "None",	.value = SMA_DISABLE,	.dpll_type = 0 },
> 	{ }
> };
> 
> static const struct ocp_selector ptp_ocp_sma_out[] = {
>-	{ .name = "10Mhz",	.value = 0x0000 },
>-	{ .name = "PHC",	.value = 0x0001 },
>-	{ .name = "MAC",	.value = 0x0002 },
>-	{ .name = "GNSS1",	.value = 0x0004 },
>-	{ .name = "GNSS2",	.value = 0x0008 },
>-	{ .name = "IRIG",	.value = 0x0010 },
>-	{ .name = "DCF",	.value = 0x0020 },
>-	{ .name = "GEN1",	.value = 0x0040 },
>-	{ .name = "GEN2",	.value = 0x0080 },
>-	{ .name = "GEN3",	.value = 0x0100 },
>-	{ .name = "GEN4",	.value = 0x0200 },
>-	{ .name = "GND",	.value = 0x2000 },
>-	{ .name = "VCC",	.value = 0x4000 },
>+	{ .name = "10Mhz",	.value = 0x0000,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_10_MHZ },
>+	{ .name = "PHC",	.value = 0x0001,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "MAC",	.value = 0x0002,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "GNSS1",	.value = 0x0004,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_1_PPS },
>+	{ .name = "GNSS2",	.value = 0x0008,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_1_PPS },
>+	{ .name = "IRIG",	.value = 0x0010,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "DCF",	.value = 0x0020,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "GEN1",	.value = 0x0040,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "GEN2",	.value = 0x0080,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "GEN3",	.value = 0x0100,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "GEN4",	.value = 0x0200,	.dpll_type = DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>+	{ .name = "GND",	.value = 0x2000,	.dpll_type = 0 },
>+	{ .name = "VCC",	.value = 0x4000,	.dpll_type = 0 },
> 	{ }
> };
> 
>@@ -4175,12 +4179,41 @@ ptp_ocp_detach(struct ptp_ocp *bp)
> 	device_unregister(&bp->dev);
> }
> 
>+static int ptp_ocp_dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr)
>+{
>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>+	int sync;
>+
>+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>+	dpll_attr_lock_status_set(attr, sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED);

get,set,confuse. This attr thing sucks, sorry :/


>+
>+	return 0;
>+}
>+
>+static int ptp_ocp_dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>+				     struct dpll_pin_attr *attr)
>+{
>+	dpll_pin_attr_type_set(attr, DPLL_PIN_TYPE_EXT);

This is exactly what I was talking about in the cover letter. This is
const, should be put into static struct and passed to
dpll_device_alloc().


>+	return 0;
>+}
>+
>+static struct dpll_device_ops dpll_ops = {
>+	.get	= ptp_ocp_dpll_get_attr,
>+};
>+
>+static struct dpll_pin_ops dpll_pin_ops = {
>+	.get	= ptp_ocp_dpll_pin_get_attr,
>+};
>+
> static int
> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> {
>+	const u8 dpll_cookie[DPLL_COOKIE_LEN] = { "OCP" };
>+	char pin_desc[PIN_DESC_LEN];
> 	struct devlink *devlink;
>+	struct dpll_pin *pin;
> 	struct ptp_ocp *bp;
>-	int err;
>+	int err, i;
> 
> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
> 	if (!devlink) {
>@@ -4230,6 +4263,20 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> 
> 	ptp_ocp_info(bp);
> 	devlink_register(devlink);
>+
>+	bp->dpll = dpll_device_alloc(&dpll_ops, DPLL_TYPE_PPS, dpll_cookie, pdev->bus->number, bp, &pdev->dev);
>+	if (!bp->dpll) {
>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>+		goto out;
>+	}
>+	dpll_device_register(bp->dpll);

You still have the 2 step init process. I believe it would be better to
just have dpll_device_create/destroy() to do it in one shot.


>+
>+	for (i = 0; i < 4; i++) {
>+		snprintf(pin_desc, PIN_DESC_LEN, "sma%d", i + 1);
>+		pin = dpll_pin_alloc(pin_desc, PIN_DESC_LEN);
>+		dpll_pin_register(bp->dpll, pin, &dpll_pin_ops, bp);

Same here, no point of having 2 step init.


>+	}
>+
> 	return 0;


Btw, did you consider having dpll instance here as and auxdev? It would
be suitable I believe. It is quite simple to do it. See following patch
as an example:

commit bd02fd76d1909637c95e8ef13e7fd1e748af910d
Author: Jiri Pirko <jiri@nvidia.com>
Date:   Mon Jul 25 10:29:17 2022 +0200

    mlxsw: core_linecards: Introduce per line card auxiliary device




> 
> out:
>@@ -4247,6 +4294,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
> 	struct devlink *devlink = priv_to_devlink(bp);
> 
>+	dpll_device_unregister(bp->dpll);
>+	dpll_device_free(bp->dpll);
> 	devlink_unregister(devlink);
> 	ptp_ocp_detach(bp);
> 	pci_disable_device(pdev);
>-- 
>2.27.0
>

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-29 21:37 ` [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions Vadim Fedorenko
@ 2022-11-30 15:21   ` Jiri Pirko
  2022-11-30 16:23     ` Jiri Pirko
  2022-12-23 16:45     ` Kubalewski, Arkadiusz
  2022-11-30 16:37   ` Jiri Pirko
  1 sibling, 2 replies; 87+ messages in thread
From: Jiri Pirko @ 2022-11-30 15:21 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Milena Olech, Michal Michalik

Tue, Nov 29, 2022 at 10:37:22PM CET, vfedorenko@novek.ru wrote:
>From: Vadim Fedorenko <vadfed@fb.com>
>
>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@fb.com>
>---
> MAINTAINERS                 |   8 +
> drivers/Kconfig             |   2 +
> drivers/Makefile            |   1 +
> drivers/dpll/Kconfig        |   7 +
> drivers/dpll/Makefile       |  11 +
> drivers/dpll/dpll_core.c    | 760 ++++++++++++++++++++++++++++
> drivers/dpll/dpll_core.h    | 176 +++++++
> drivers/dpll/dpll_netlink.c | 963 ++++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h |  24 +
> include/linux/dpll.h        | 261 ++++++++++
> include/uapi/linux/dpll.h   | 263 ++++++++++
> 11 files changed, 2476 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 61fe86968111..79b76cca9620 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6343,6 +6343,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 19ee995bd0ae..a3e00294a995 100644
>--- a/drivers/Kconfig
>+++ b/drivers/Kconfig
>@@ -239,4 +239,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..3391deb08014
>--- /dev/null
>+++ b/drivers/dpll/Makefile
>@@ -0,0 +1,11 @@
>+# SPDX-License-Identifier: GPL-2.0
>+#
>+# Makefile for DPLL drivers.
>+#
>+
>+obj-$(CONFIG_DPLL)          += dpll_sys.o
>+dpll_sys-y                  += dpll_attr.o
>+dpll_sys-y                  += dpll_core.o
>+dpll_sys-y                  += dpll_netlink.o
>+dpll_sys-y                  += dpll_pin_attr.o
>+
>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..013ac4150583
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,760 @@
>+// 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[PIN_DESC_LEN];
>+};
>+
>+/**
>+ * 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
>+ * @cookie:	unique identifier (cookie) of a dpll
>+ * @dev_driver_idx: provided by driver for
>+ */
>+struct dpll_device {

Just "dpll" please. Device somehow indicates this is managing device on
the bus. It is misleading.


>+	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;
>+	u8 cookie[DPLL_COOKIE_LEN];
>+	u8 dev_driver_idx;
>+};
>+
>+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 pin_ref_dpll {

Namespace


>+	struct dpll_device *dpll;
>+	struct dpll_pin_ops *ops;
>+	void *priv;
>+};
>+
>+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;
>+}
>+
>+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_cookie(u8 cookie[DPLL_COOKIE_LEN],
>+					      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 (!memcmp(dpll->cookie, cookie, DPLL_COOKIE_LEN)) {
>+			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_cookie);
>+
>+static void dpll_device_release(struct device *dev)
>+{
>+	struct dpll_device *dpll;
>+
>+	dpll = to_dpll_device(dev);
>+
>+	dpll_device_unregister(dpll);
>+	dpll_device_free(dpll);
>+}
>+
>+static struct class dpll_class = {
>+	.name = "dpll",
>+	.dev_release = dpll_device_release,
>+};
>+
>+static const char *dpll_type_name[__DPLL_TYPE_MAX] = {
>+	[DPLL_TYPE_UNSPEC] = "",
>+	[DPLL_TYPE_PPS] = "PPS",
>+	[DPLL_TYPE_EEC] = "EEC",
>+};
>+
>+static const char *dpll_type_str(enum dpll_type type)
>+{
>+	if (type >= DPLL_TYPE_UNSPEC && type <= DPLL_TYPE_MAX)
>+		return dpll_type_name[type];
>+	else
>+		return "";
>+}
>+
>+struct dpll_device
>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>+		   const u8 cookie[DPLL_COOKIE_LEN], u8 idx,

This cooking thing should be removed alongside with the getter. Driver
should not need it.


>+		   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 = idx;
>+	memcpy(dpll->cookie, cookie, sizeof(dpll->cookie));
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,

You have idx and id? Why?


>+		       xa_limit_16b, GFP_KERNEL);
>+	if (ret)
>+		goto error;
>+	dev_set_name(&dpll->dev, "dpll-%s-%s-%s%d", dev_driver_string(parent),
>+		     dev_name(parent), type ? dpll_type_str(type) : "", idx);

Odd. You encode parent device name into a name. What do we have device
hierarchy for? Also, why to encode "type_str"? Does no make sense to me.


>+	dpll->priv = priv;
>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>+	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);
>+
>+void dpll_device_register(struct dpll_device *dpll)
>+{
>+	ASSERT_DPLL_NOT_REGISTERED(dpll);
>+
>+	mutex_lock(&dpll_device_xa_lock);
>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+	mutex_unlock(&dpll_device_xa_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_register);
>+
>+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);
>+
>+u32 dpll_id(struct dpll_device *dpll)
>+{
>+	return dpll->id;
>+}
>+EXPORT_SYMBOL_GPL(dpll_id);
>+
>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)

This is odd. You just need pin here, no need to pass dpll.


>+{
>+	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);

General comment to this getter helpers. Why do you need them? Why the
driver cares about name, abou idx and other attributes. Why driver needs
to iterate over pins?
This should not be needed, please remove.



>+}
>+EXPORT_SYMBOL_GPL(dpll_dev_name);
>+
>+struct dpll_pin *dpll_pin_alloc(const char *description, size_t desc_len)


Why do you need description? In your pps example, you pass "SMA". Isn't
that rather a pin type? Const attribute of a pin?
I can understand a need for "label", if you have 2 SMA connectors
labeled "port 1" and "port 2", pass that.

Also, it's a string, you don't need len.
Also, you can have this as vararg to make caller's live easier.



>+{
>+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
>+
>+	if (!pin)
>+		return ERR_PTR(-ENOMEM);
>+	if (desc_len > PIN_DESC_LEN)
>+		return ERR_PTR(-EINVAL);
>+
>+	strncpy(pin->description, description, PIN_DESC_LEN);
>+	if (desc_len == PIN_DESC_LEN)
>+		pin->description[PIN_DESC_LEN - 1] = '\0';
>+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
>+
>+	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, 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 pin_ref_dpll_add(struct dpll_pin *pin, struct dpll_device *dpll,
>+			    struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct pin_ref_dpll *ref, *pos;
>+	unsigned long index;
>+	u32 idx;
>+
>+	ref = kzalloc(sizeof(struct pin_ref_dpll), 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 pin_ref_dpll_del(struct dpll_pin *pin, struct dpll_device *dpll)
>+{
>+	struct pin_ref_dpll *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;
>+		}
>+	}
>+}

As I wrote in the reply to the cover, I believe the sharing of pins
between instances is wrong, then you can avoid this ref dance.



>+
>+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 = pin_ref_dpll_add(pin, dpll, ops, priv);
>+		if (ret)
>+			pin_deregister_from_xa(&dpll->pins, pin);
>+	}
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_ADD, pin);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>+
>+int
>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>+			 struct dpll_device *dpll, u32 pin_idx,
>+			 struct dpll_pin_ops *ops, void *priv)
>+{
>+	struct dpll_pin *pin;
>+	int ret;
>+
>+	mutex_lock(&dpll_pin_owner->lock);
>+	pin = dpll_pin_get_by_idx(dpll_pin_owner, pin_idx);
>+	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)
>+		pin_ref_dpll_del(pin, dpll);
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_DEL, pin);
>+
>+	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,
>+			    struct dpll_pin *parent_pin, struct dpll_pin *pin,
>+			    struct dpll_pin_ops *ops, void *priv)
>+{
>+	int ret;
>+
>+	if (!parent_pin || !pin)
>+		return -EINVAL;
>+
>+	mutex_lock(&dpll->lock);
>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>+	if (!ret)
>+		ret = pin_ref_dpll_add(pin, dpll, ops, priv);
>+	if (!ret)
>+		pin->parent_pin = parent_pin;
>+	mutex_unlock(&dpll->lock);
>+	if (!ret)
>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_ADD, pin);
>+
>+	return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
>+
>+struct dpll_pin
>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char *description)

Why on earth would driver need this helper? If it does, something is
wrong. Please remove.



>+{
>+	struct dpll_pin *pos, *pin = NULL;
>+	unsigned long index;
>+
>+	mutex_lock(&dpll->lock);
>+	xa_for_each(&dpll->pins, index, pos) {
>+		if (!strncmp(pos->description, description, PIN_DESC_LEN)) {
>+			pin = pos;
>+			break;
>+		}
>+	}
>+	mutex_unlock(&dpll->lock);
>+
>+	return pin;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_get_by_description);
>+
>+static 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;
>+}
>+
>+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);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_get_by_idx);

Again, please remove these heplers. Driver should be happy only with
opaque struct dpll and struct dpll_pin


>+
>+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);
>+}
>+
>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long *index)
>+{
>+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>+}
>+
>+struct dpll_device *dpll_first(unsigned long *index)
>+{
>+	*index = 0;
>+
>+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>+}
>+
>+struct dpll_device *dpll_next(unsigned long *index)
>+{
>+	return xa_find_after(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>+}
>+
>+static int
>+dpll_notify_pin_change_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>+			    const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_event_change change;
>+	int ret = 0;
>+
>+	if (dpll_pin_attr_valid(DPLLA_PIN_TYPE, attr)) {
>+		change = DPLL_CHANGE_PIN_TYPE;
>+		ret = dpll_notify_device_change(dpll, change, pin);
>+	}
>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_SIGNAL_TYPE, attr)) {
>+		change = DPLL_CHANGE_PIN_SIGNAL_TYPE;
>+		ret = dpll_notify_device_change(dpll, change, pin);
>+	}
>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_CUSTOM_FREQ, attr)) {
>+		change = DPLL_CHANGE_PIN_CUSTOM_FREQ;
>+		ret = dpll_notify_device_change(dpll, change, pin);
>+	}
>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_STATE, attr)) {
>+		change = DPLL_CHANGE_PIN_STATE;
>+		ret = dpll_notify_device_change(dpll, change, pin);
>+	}
>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_PRIO, attr)) {
>+		change = DPLL_CHANGE_PIN_PRIO;
>+		ret = dpll_notify_device_change(dpll, change, pin);
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_notify_device_change_attr(struct dpll_device *dpll,
>+					  const struct dpll_attr *attr)
>+{
>+	int ret = 0;
>+
>+	if (dpll_attr_valid(DPLLA_MODE, attr))
>+		ret = dpll_notify_device_change(dpll, DPLL_CHANGE_MODE, NULL);
>+	if (!ret && dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr))
>+		ret = dpll_notify_device_change(dpll, DPLL_CHANGE_SOURCE_PIN,
>+						NULL);
>+	return ret;
>+}
>+
>+static struct pin_ref_dpll
>+*dpll_pin_find_ref(struct dpll_device *dpll, struct dpll_pin *pin)
>+{
>+	struct pin_ref_dpll *ref;
>+	unsigned long index;
>+
>+	xa_for_each(&pin->ref_dplls, index, ref) {
>+		if (ref->dpll != dpll)
>+			continue;
>+		else
>+			return ref;
>+	}
>+
>+	return NULL;
>+}
>+
>+static int
>+dpll_pin_set_attr_single_ref(struct dpll_device *dpll, struct dpll_pin *pin,
>+			     const struct dpll_pin_attr *attr)
>+{
>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	mutex_lock(&ref->dpll->lock);
>+	ret = ref->ops->set(ref->dpll, pin, attr);
>+	if (!ret)
>+		dpll_notify_pin_change_attr(dpll, pin, attr);
>+	mutex_unlock(&ref->dpll->lock);
>+
>+	return ret;
>+}
>+
>+static int
>+dpll_pin_set_attr_all_refs(struct dpll_pin *pin,
>+			   const struct dpll_pin_attr *attr)
>+{
>+	struct pin_ref_dpll *ref;
>+	unsigned long index;
>+	int ret;
>+
>+	xa_for_each(&pin->ref_dplls, index, ref) {
>+		if (!ref->dpll)
>+			return -EFAULT;
>+		if (!ref || !ref->ops || !ref->ops->set)
>+			return -EOPNOTSUPP;
>+		mutex_lock(&ref->dpll->lock);
>+		ret = ref->ops->set(ref->dpll, pin, attr);
>+		mutex_unlock(&ref->dpll->lock);
>+		if (!ret)
>+			dpll_notify_pin_change_attr(ref->dpll, pin, attr);
>+	}
>+
>+	return ret;
>+}
>+
>+int dpll_pin_set_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      const struct dpll_pin_attr *attr)
>+{
>+	struct dpll_pin_attr *tmp_attr;
>+	int ret;
>+
>+	tmp_attr = dpll_pin_attr_alloc();
>+	if (!tmp_attr)
>+		return -ENOMEM;
>+	ret = dpll_pin_attr_prep_common(tmp_attr, attr);
>+	if (ret < 0)
>+		goto tmp_free;
>+	if (ret == PIN_ATTR_CHANGE) {
>+		ret = dpll_pin_set_attr_all_refs(pin, tmp_attr);
>+		if (ret)
>+			goto tmp_free;
>+	}
>+
>+	ret = dpll_pin_attr_prep_exclusive(tmp_attr, attr);
>+	if (ret < 0)
>+		goto tmp_free;
>+	if (ret == PIN_ATTR_CHANGE)
>+		ret = dpll_pin_set_attr_single_ref(dpll, pin, tmp_attr);
>+
>+tmp_free:
>+	dpll_pin_attr_free(tmp_attr);
>+	return ret;
>+}
>+
>+int dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      struct dpll_pin_attr *attr)
>+{
>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>+	int ret;
>+
>+	if (!ref)
>+		return -ENODEV;
>+	if (!ref->ops || !ref->ops->get)
>+		return -EOPNOTSUPP;
>+
>+	ret = ref->ops->get(dpll, pin, attr);
>+	if (ret)
>+		return -EAGAIN;
>+
>+	return ret;
>+}
>+
>+const char *dpll_pin_get_description(struct dpll_pin *pin)
>+{
>+	return pin->description;
>+}
>+
>+struct dpll_pin *dpll_pin_get_parent(struct dpll_pin *pin)
>+{
>+	return pin->parent_pin;
>+}

These helpers should not exist. Not needed for anything good.



>+
>+int dpll_set_attr(struct dpll_device *dpll, const struct dpll_attr *attr)
>+{
>+	int ret;
>+
>+	if (dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr)) {
>+		struct pin_ref_dpll *ref;
>+		struct dpll_pin *pin;
>+		u32 source_idx;
>+
>+		ret = dpll_attr_source_idx_get(attr, &source_idx);
>+		if (ret)
>+			return -EINVAL;
>+		pin = dpll_pin_get_by_idx(dpll, source_idx);
>+		if (!pin)
>+			return -ENXIO;
>+		ref = dpll_pin_find_ref(dpll, pin);
>+		if (!ref || !ref->ops)
>+			return -EFAULT;
>+		if (!ref->ops->select)
>+			return -ENODEV;
>+		dpll_lock(ref->dpll);
>+		ret = ref->ops->select(ref->dpll, pin);
>+		dpll_unlock(ref->dpll);
>+		if (ret)
>+			return -EINVAL;
>+		dpll_notify_device_change_attr(dpll, attr);
>+	}
>+
>+	if (dpll_attr_valid(DPLLA_MODE, attr)) {
>+		dpll_lock(dpll);
>+		ret = dpll->ops->set(dpll, attr);
>+		dpll_unlock(dpll);
>+		if (ret)
>+			return -EINVAL;
>+	}
>+	dpll_notify_device_change_attr(dpll, attr);
>+
>+	return ret;
>+}
>+
>+int dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr)
>+{
>+	if (!dpll)
>+		return -ENODEV;
>+	if (!dpll->ops || !dpll->ops->get)
>+		return -EOPNOTSUPP;
>+	if (dpll->ops->get(dpll, attr))
>+		return -EAGAIN;
>+
>+	return 0;
>+}
>+
>+void dpll_lock(struct dpll_device *dpll)
>+{
>+	mutex_lock(&dpll->lock);
>+}
>+
>+void dpll_unlock(struct dpll_device *dpll)
>+{
>+	mutex_unlock(&dpll->lock);
>+}
>+
>+void *dpll_priv(struct dpll_device *dpll)
>+{
>+	return dpll->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_priv);

Why do you need this?


>+
>+void *dpll_pin_priv(struct dpll_device *dpll, struct dpll_pin *pin)
>+{
>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>+
>+	if (!ref)
>+		return NULL;
>+
>+	return ref->priv;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_priv);

And this.


>+
>+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..9cefecdfc47b
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.h
>@@ -0,0 +1,176 @@
>+/* 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)
>+
>+#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))
>+
>+
>+/**
>+ * dpll_device_get_by_id - find dpll device by it's id
>+ * @id: dpll id
>+ *
>+ * Return: dpll_device struct if found, NULL otherwise.
>+ */

Please move the functions comments into .c file.


>+struct dpll_device *dpll_device_get_by_id(int id);
>+
>+/**
>+ * dpll_device_get_by_name - find dpll device by it's id
>+ * @name: dpll name
>+ *
>+ * Return: dpll_device struct if found, NULL otherwise.
>+ */
>+struct dpll_device *dpll_device_get_by_name(const char *name);
>+
>+/**
>+ * 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);
>+
>+/**
>+ * 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);
>+
>+/**
>+ * 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);
>+
>+/**
>+ * 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);
>+
>+/**
>+ * 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);
>+
>+/**
>+ * dpll_id - return dpll id
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: dpll id.
>+ */
>+u32 dpll_id(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_pin_idx - return dpll name
>+ * @dpll: registered dpll pointer
>+ *
>+ * Return: dpll name.
>+ */
>+const char *dpll_dev_name(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_lock - locks the dpll using internal mutex
>+ * @dpll: registered dpll pointer
>+ */
>+void dpll_lock(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_unlock - unlocks the dpll using internal mutex
>+ * @dpll: registered dpll pointer
>+ */
>+void dpll_unlock(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_set_attr - handler for dpll subsystem: dpll set attributes
>+ * @dpll: registered dpll pointer
>+ * @attr: dpll attributes
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_set_attr(struct dpll_device *dpll, const struct dpll_attr *attr);
>+
>+/**
>+ * dpll_get_attr - handler for dpll subsystem: dpll get attributes
>+ * @dpll: registered dpll pointer
>+ * @attr: dpll attributes
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr);
>+
>+/**
>+ * dpll_pin_idx - return dpll id
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ *
>+ * Return: dpll id.
>+ */
>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_get_attr - handler for dpll subsystem: dpll pin get attributes
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @attr: dpll pin attributes
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      struct dpll_pin_attr *attr);
>+
>+/**
>+ * dpll_pin_get_description - provide pin's description string
>+ * @pin: registered pin pointer
>+ *
>+ * Return: pointer to a description string.
>+ */
>+const char *dpll_pin_get_description(struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_get_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_get_parent(struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_set_attr - handler for dpll subsystem: dpll pin get attributes
>+ * @dpll: registered dpll pointer
>+ * @pin: registered pin pointer
>+ * @attr: dpll pin attributes
>+ *
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_pin_set_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>+		      const struct dpll_pin_attr *attr);
>+
>+#endif
>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>new file mode 100644
>index 000000000000..9a1f682a42ac
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -0,0 +1,963 @@
>+// 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_DUMP_FILTER]	= { .type = NLA_U32 },
>+	[DPLLA_NETIFINDEX]	= { .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_TYPE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>+	[DPLLA_PIN_STATE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>+};
>+
>+struct dpll_param {
>+	struct netlink_callback *cb;
>+	struct sk_buff *msg;
>+	struct dpll_device *dpll;
>+	struct dpll_pin *pin;
>+	enum dpll_event_change change_type;
>+};
>+
>+struct dpll_dump_ctx {
>+	int dump_filter;
>+};
>+
>+typedef int (*cb_t)(struct dpll_param *);
>+
>+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_attr *attr)
>+{
>+	enum dpll_mode m = dpll_attr_mode_get(attr);
>+
>+	if (m == DPLL_MODE_UNSPEC)
>+		return 0;
>+
>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>+}
>+
>+static int dpll_msg_add_modes_supported(struct sk_buff *msg,
>+					const struct dpll_attr *attr)
>+{
>+	enum dpll_mode i;
>+	int  ret = 0;
>+
>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>+		if (dpll_attr_mode_supported(attr, i)) {
>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>+			if (ret)
>+				return -EMSGSIZE;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_attr *attr)
>+{
>+	u32 source_idx;
>+
>+	if (dpll_attr_source_idx_get(attr, &source_idx))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_netifindex(struct sk_buff *msg, struct dpll_attr *attr)
>+{
>+	unsigned int netifindex; // TODO: Should be u32?
>+
>+	if (dpll_attr_netifindex_get(attr, &netifindex))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_NETIFINDEX, netifindex))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_attr *attr)
>+{
>+	enum dpll_lock_status s = dpll_attr_lock_status_get(attr);
>+
>+	if (s == DPLL_LOCK_STATUS_UNSPEC)
>+		return 0;
>+	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_attr *attr)
>+{
>+	s32 temp;
>+
>+	if (dpll_attr_temp_get(attr, &temp))
>+		return 0;
>+	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, enum dplla attr,
>+				   enum dpll_pin_type type)
>+{
>+	if (nla_put_s32(msg, attr, type))
>+		return -EMSGSIZE;


Please avoid these pointless helpers and call nla_put_*() directly.


>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_pin_type t = dpll_pin_attr_type_get(attr);
>+
>+	if (t == DPLL_PIN_TYPE_UNSPEC)
>+		return 0;
>+
>+	return __dpll_msg_add_pin_type(msg, DPLLA_PIN_TYPE, t);
>+}
>+
>+static int dpll_msg_add_pin_types_supported(struct sk_buff *msg,
>+					    const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_pin_type i;
>+	int ret;
>+
>+	for (i = DPLL_PIN_TYPE_UNSPEC + 1; i <= DPLL_PIN_TYPE_MAX; i++) {
>+		if (dpll_pin_attr_type_supported(attr, i)) {
>+			ret = __dpll_msg_add_pin_type(msg,
>+						      DPLLA_PIN_TYPE_SUPPORTED,
>+						      i);
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	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_pin_attr *attr)
>+{
>+	enum dpll_pin_signal_type t = dpll_pin_attr_signal_type_get(attr);
>+
>+	if (t == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>+		return 0;
>+
>+	return __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>+}
>+
>+static int
>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>+					const struct dpll_pin_attr *attr)
>+{
>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>+	enum dpll_pin_signal_type i;
>+	int ret;
>+
>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>+		if (dpll_pin_attr_signal_type_supported(attr, i)) {
>+			ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_custom_freq(struct sk_buff *msg,
>+					const struct dpll_pin_attr *attr)
>+{
>+	u32 freq;
>+
>+	if (dpll_pin_attr_custom_freq_get(attr, &freq))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_states(struct sk_buff *msg,
>+				   const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_pin_state i;
>+
>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>+		if (dpll_pin_attr_state_enabled(attr, i))
>+			if (nla_put_s32(msg, DPLLA_PIN_STATE, i))
>+				return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_states_supported(struct sk_buff *msg,
>+					     const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_pin_state i;
>+
>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>+		if (dpll_pin_attr_state_supported(attr, i))
>+			if (nla_put_s32(msg, DPLLA_PIN_STATE_SUPPORTED, i))
>+				return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_pin_attr *attr)
>+{
>+	u32 prio;
>+
>+	if (dpll_pin_attr_prio_get(attr, &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_pin_attr *attr)
>+{
>+	unsigned int netifindex; // TODO: Should be u32?
>+
>+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_event_change_type(struct sk_buff *msg,
>+			       enum dpll_event_change event)
>+{
>+	if (nla_put_s32(msg, DPLLA_CHANGE_TYPE, event))
>+		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_attr *attr = dpll_pin_attr_alloc();
>+	struct dpll_pin *parent = NULL;
>+	int ret;
>+
>+	if (!attr)
>+		return -ENOMEM;
>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_description(msg, dpll_pin_get_description(pin));
>+	if (ret)
>+		goto out;
>+	parent = dpll_pin_get_parent(pin);
>+	if (parent) {
>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>+								    parent));
>+		if (ret)
>+			goto out;
>+	}
>+	ret = dpll_pin_get_attr(dpll, pin, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_type(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_types_supported(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_signal_type(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_signal_types_supported(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_custom_freq(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_states(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_states_supported(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_prio(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_netifindex(msg, attr);
>+	if (ret)
>+		goto out;
>+out:
>+	dpll_pin_attr_free(attr);
>+
>+	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)
>+{
>+	struct dpll_attr *attr = dpll_attr_alloc();
>+	int ret = dpll_get_attr(dpll, attr);
>+
>+	if (ret)
>+		return -EAGAIN;
>+	if (dpll_msg_add_source_pin(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_temp(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_lock_status(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_mode(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_modes_supported(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_netifindex(msg, attr))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+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_DUMP_FILTER_STATUS) {
>+		ret = __dpll_cmd_dump_status(msg, dpll);
>+		if (ret)
>+			goto out_unlock;
>+	}
>+	if (dump_filter & DPLL_DUMP_FILTER_PINS)
>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>+	dpll_unlock(dpll);
>+
>+	return ret;
>+out_unlock:
>+	dpll_unlock(dpll);
>+	return ret;
>+}
>+
>+static enum dpll_pin_type dpll_msg_read_pin_type(struct nlattr *a)
>+{
>+	return nla_get_s32(a);


No need to have this pointless boilerplate helpers, just call nla_get_x()
directly.


>+}
>+
>+static enum dpll_pin_signal_type dpll_msg_read_pin_sig_type(struct nlattr *a)
>+{
>+	return nla_get_s32(a);
>+}
>+
>+static u32 dpll_msg_read_pin_custom_freq(struct nlattr *a)
>+{
>+	return nla_get_u32(a);
>+}
>+
>+static enum dpll_pin_state dpll_msg_read_pin_state(struct nlattr *a)
>+{
>+	return nla_get_s32(a);
>+}
>+
>+static u32 dpll_msg_read_pin_prio(struct nlattr *a)
>+{
>+	return nla_get_u32(a);
>+}
>+
>+static u32 dpll_msg_read_dump_filter(struct nlattr *a)
>+{
>+	return nla_get_u32(a);
>+}
>+
>+static int
>+dpll_pin_attr_from_nlattr(struct dpll_pin_attr *pa, struct genl_info *info)
>+{
>+	enum dpll_pin_signal_type st;
>+	enum dpll_pin_state state;
>+	enum dpll_pin_type t;
>+	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_TYPE:

Does not make sense to me. Why do you allow to set the pin type? That
should be something constant, according to the hardware architecture.


>+			t = dpll_msg_read_pin_type(a);
>+			ret = dpll_pin_attr_type_set(pa, t);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_SIGNAL_TYPE:
>+			st = dpll_msg_read_pin_sig_type(a);
>+			ret = dpll_pin_attr_signal_type_set(pa, st);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_CUSTOM_FREQ:
>+			freq = dpll_msg_read_pin_custom_freq(a);
>+			ret = dpll_pin_attr_custom_freq_set(pa, freq);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_STATE:
>+			state = dpll_msg_read_pin_state(a);
>+			ret = dpll_pin_attr_state_set(pa, state);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_PRIO:
>+			prio = dpll_msg_read_pin_prio(a);
>+			ret = dpll_pin_attr_prio_set(pa, 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_pin_attr *old = NULL, *new = NULL, *delta = NULL;
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	struct nlattr **attrs = info->attrs;
>+	struct dpll_pin *pin;
>+	int ret, pin_id;
>+
>+	if (!attrs[DPLLA_PIN_IDX])
>+		return -EINVAL;
>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>+	old = dpll_pin_attr_alloc();
>+	new = dpll_pin_attr_alloc();
>+	delta = dpll_pin_attr_alloc();
>+	if (!old || !new || !delta) {
>+		ret = -ENOMEM;
>+		goto mem_free;
>+	}
>+	dpll_lock(dpll);
>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>+	if (!pin) {
>+		ret = -ENODEV;
>+		goto mem_free_unlock;
>+	}
>+	ret = dpll_pin_get_attr(dpll, pin, old);
>+	if (ret)
>+		goto mem_free_unlock;
>+	ret = dpll_pin_attr_from_nlattr(new, info);
>+	if (ret)
>+		goto mem_free_unlock;
>+	ret = dpll_pin_attr_delta(delta, new, old);
>+	dpll_unlock(dpll);
>+	if (!ret)
>+		ret = dpll_pin_set_attr(dpll, pin, delta);
>+	else
>+		ret = -EINVAL;
>+
>+	dpll_pin_attr_free(delta);
>+	dpll_pin_attr_free(new);
>+	dpll_pin_attr_free(old);
>+
>+	return ret;
>+
>+mem_free_unlock:
>+	dpll_unlock(dpll);
>+mem_free:
>+	dpll_pin_attr_free(delta);
>+	dpll_pin_attr_free(new);
>+	dpll_pin_attr_free(old);
>+	return ret;
>+}
>+
>+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_attr_from_nlattr(struct dpll_attr *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_attr_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_attr_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_attr *old = NULL, *new = NULL, *delta = NULL;
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	int ret;
>+
>+	old = dpll_attr_alloc();
>+	new = dpll_attr_alloc();
>+	delta = dpll_attr_alloc();
>+	if (!old || !new || !delta) {
>+		ret = -ENOMEM;
>+		goto mem_free;
>+	}
>+	dpll_lock(dpll);
>+	ret = dpll_get_attr(dpll, old);
>+	dpll_unlock(dpll);
>+	if (!ret) {
>+		dpll_attr_from_nlattr(new, info);
>+		ret = dpll_attr_delta(delta, new, old);
>+		if (!ret)
>+			ret = dpll_set_attr(dpll, delta);
>+	}
>+
>+mem_free:
>+	dpll_attr_free(old);
>+	dpll_attr_free(new);
>+	dpll_attr_free(delta);
>+
>+	return ret;
>+}
>+
>+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_DUMP_FILTER])

It's not a "dump" filter for "get" callback. Btw, why do you need this
filtering at all? Do you expect some significant amount of pins assigned
to dpll so you need to filter them out? If not, leave the filtering
mechanism out for now to make things easier.



>+		dump_filter =
>+			dpll_msg_read_dump_filter(attrs[DPLLA_DUMP_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_DUMP_FILTER];
>+
>+	if (attr)
>+		ctx->dump_filter = dpll_msg_read_dump_filter(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);

You are missing some locking here. What is protecting the driver from
unregistering the instance right at the time you have this
pointer returned?

You need some reference counting and locking scheme to be defined and
used.


>+		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]);

I still think that we should have 1 handle for dpll.
We don't need DPLLA_ID. It is not stable, not sure anyone will use it.
Why don't you have bus/name handle tuple similar to how we do it in
devlink? It proved to be working good over the years. Check out:
DEVLINK_ATTR_BUS_NAME
DEVLINK_ATTR_DEV_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 dpll_param *p)
>+{
>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_name(p->msg, dpll_dev_name(p->dpll));
>+
>+	return ret;
>+}
>+
>+static int dpll_event_device_change(struct dpll_param *p)
>+{
>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_event_change_type(p->msg, p->change_type);
>+	if (ret)
>+		return ret;
>+	switch (p->change_type)	{
>+	case DPLL_CHANGE_PIN_ADD:
>+	case DPLL_CHANGE_PIN_DEL:
>+	case DPLL_CHANGE_PIN_TYPE:
>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>+	case DPLL_CHANGE_PIN_STATE:
>+	case DPLL_CHANGE_PIN_PRIO:
>+		ret = dpll_msg_add_pin_idx(p->msg, dpll_pin_idx(p->dpll, p->pin));
>+		break;
>+	default:
>+		break;
>+	}
>+
>+	return ret;
>+}
>+
>+static const cb_t event_cb[] = {
>+	[DPLL_EVENT_DEVICE_CREATE]	= dpll_event_device_id,
>+	[DPLL_EVENT_DEVICE_DELETE]	= dpll_event_device_id,
>+	[DPLL_EVENT_DEVICE_CHANGE]	= dpll_event_device_change,
>+};
>+
>+/*
>+ * Generic netlink DPLL event encoding
>+ */
>+static int dpll_send_event(enum dpll_event event, struct dpll_param *p)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+	p->msg = msg;
>+
>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>+	if (!hdr)
>+		goto out_free_msg;
>+
>+	ret = event_cb[event](p);
>+	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)
>+{
>+	struct dpll_param p = { .dpll = dpll };
>+
>+	return dpll_send_event(DPLL_EVENT_DEVICE_CREATE, &p);
>+}
>+
>+int dpll_notify_device_delete(struct dpll_device *dpll)
>+{
>+	struct dpll_param p = { .dpll = dpll };
>+
>+	return dpll_send_event(DPLL_EVENT_DEVICE_DELETE, &p);
>+}
>+
>+int dpll_notify_device_change(struct dpll_device *dpll,
>+			      enum dpll_event_change event,
>+			      struct dpll_pin *pin)
>+{
>+	struct dpll_param p = { .dpll = dpll,
>+				.change_type = event,
>+				.pin = pin };
>+
>+	return dpll_send_event(DPLL_EVENT_DEVICE_CHANGE, &p);
>+}
>+EXPORT_SYMBOL_GPL(dpll_notify_device_change);
>+
>+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..a9cbf260624d
>--- /dev/null
>+++ b/include/linux/dpll.h
>@@ -0,0 +1,261 @@
>+/* 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/dpll_attr.h>
>+#include <linux/device.h>
>+
>+struct dpll_device;
>+struct dpll_pin;
>+
>+#define DPLL_COOKIE_LEN		10
>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)

Plese maintain the namespace prefix.


>+struct dpll_device_ops {
>+	int (*get)(struct dpll_device *dpll, struct dpll_attr *attr);
>+	int (*set)(struct dpll_device *dpll, const struct dpll_attr *attr);
>+};
>+
>+struct dpll_pin_ops {
>+	int (*get)(struct dpll_device *dpll, struct dpll_pin *pin,
>+		   struct dpll_pin_attr *attr);
>+	int (*set)(struct dpll_device *dpll, struct dpll_pin *pin,
>+		   const struct dpll_pin_attr *attr);
>+	int (*select)(struct dpll_device *dpll, struct dpll_pin *pin);
>+};
>+
>+enum dpll_type {
>+	DPLL_TYPE_UNSPEC,

You are not in UAPI, no need of unspec here.


>+	DPLL_TYPE_PPS,
>+	DPLL_TYPE_EEC,

What exactly are these? Some comment would be really good here.



>+
>+	__DPLL_TYPE_MAX
>+};
>+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
>+
>+/**
>+ * dpll_device_alloc - allocate memory for a new dpll_device object
>+ * @ops: pointer to dpll operations structure
>+ * @type: type of a dpll being allocated
>+ * @cookie: a system unique number for a device
>+ * @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: cookie, type and dev_driver_idx.
>+ * Finding allocated and registered dpll device is also possible with
>+ * the: cookie, 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 u8 cookie[DPLL_COOKIE_LEN], u8 dev_driver_idx,
>+		   void *priv, struct device *parent);
>+
>+/**
>+ * dpll_device_register - registers allocated dpll
>+ * @dpll: pointer to dpll
>+ *
>+ * Register the dpll on the dpll subsystem, make it available for netlink
>+ * API users.
>+ */
>+void dpll_device_register(struct dpll_device *dpll);
>+
>+/**
>+ * 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(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(struct dpll_device *dpll, 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
>+ * @pin_idx: index of a pin on dpll device (@dpll_pin_owner)
>+ *	     that is being registered on 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, u32 pin_idx,
>+			 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
>+ * @desc_len: number of chars in description
>+ *
>+ * 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, size_t desc_len);
>+
>+
>+/**
>+ * 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: pointer to object to register pin with
>+ * @pin: pointer to allocated pin object being deregistered from dpll
>+ * @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,
>+			    struct dpll_pin *parent_pin, struct dpll_pin *pin,
>+			    struct dpll_pin_ops *ops, void *priv);
>+/**
>+ * dpll_device_get_by_cookie - find a dpll by its cookie
>+ * @cookie: cookie of dpll to search for, 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_cookie(u8 cookie[DPLL_COOKIE_LEN],
>+					      enum dpll_type type, u8 idx);
>+
>+/**
>+ * dpll_pin_get_by_description - find a pin by its description
>+ * @dpll: dpll device pointer
>+ * @description: string description 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_description(struct dpll_device *dpll,
>+					     const char *description);
>+
>+/**
>+ * 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);
>+
>+int dpll_notify_device_change(struct dpll_device *dpll,
>+			      enum dpll_event_change event,
>+			      struct dpll_pin *pin);
>+#endif
>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>new file mode 100644
>index 000000000000..16278618d59d
>--- /dev/null
>+++ b/include/uapi/linux/dpll.h
>@@ -0,0 +1,263 @@
>+/* 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 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_DUMP_FILTER_PINS	1
>+#define DPLL_DUMP_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_DUMP_FILTER - filter bitmask (int, sum of DPLL_DUMP_FILTER_* defines)
>+ * @DPLLA_NETIFINDEX - related network interface index
>+ * @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_TYPE_SUPPORTED - pin types supported (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_STATE - state of pin's capabilities (enum dpll_pin_state)
>+ * @DPLLA_PIN_STATE_SUPPORTED - available pin's capabilities
>+ *	(enum dpll_pin_state)
>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>+ * @DPLLA_CHANGE_TYPE - type of device change event
>+ *	(enum dpll_change_type)
>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>+ **/
>+enum dplla {
>+	DPLLA_UNSPEC,
>+	DPLLA_ID,
>+	DPLLA_NAME,
>+	DPLLA_MODE,
>+	DPLLA_MODE_SUPPORTED,
>+	DPLLA_SOURCE_PIN_IDX,
>+	DPLLA_LOCK_STATUS,
>+	DPLLA_TEMP,
>+	DPLLA_DUMP_FILTER,
>+	DPLLA_NETIFINDEX,
>+	DPLLA_PIN,
>+	DPLLA_PIN_IDX,
>+	DPLLA_PIN_DESCRIPTION,
>+	DPLLA_PIN_TYPE,
>+	DPLLA_PIN_TYPE_SUPPORTED,
>+	DPLLA_PIN_SIGNAL_TYPE,
>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>+	DPLLA_PIN_CUSTOM_FREQ,
>+	DPLLA_PIN_STATE,
>+	DPLLA_PIN_STATE_SUPPORTED,
>+	DPLLA_PIN_PRIO,
>+	DPLLA_PIN_PARENT_IDX,
>+	DPLLA_CHANGE_TYPE,
>+	DPLLA_PIN_NETIFINDEX,
>+	__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,

Good.


>+
>+	__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

As I wrote in the reply to the cover letter, I don't think we should
have this.


>+ * @DPLL_PIN_TYPE_EXT - external source

Why "source" ? It could be an output too.


>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator

I don't follow why this is a PIN. It is internal clock of DPLL. It
should not be exposed as a pin.


>+ * @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,

1HZ

>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,

10000000Hz

>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,

XHz

Why don't we have this at all? There could be just u64 ATTR that carries
frequency in Hz.


We are missing signal type for SYNCE_ETH_PORT pin type. Is it supposed
to be DPLL_PIN_SIGNAL_TYPE_UNSPEC? I think it might be if we stick with
having this enum.


>+
>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>+};
>+
>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>+
>+/* dpll_pin_state - available pin states
>+ *
>+ * @DPLL_PIN_STATE_UNSPEC - unspecified value
>+ * @DPLL_PIN_STATE_CONNECTED - pin connected
>+ * @DPLL_PIN_STATE_DISCONNECTED - pin disconnected
>+ * @DPLL_PIN_STATE_SOURCE - pin used as an input pin
>+ * @DPLL_PIN_STATE_OUTPUT - pin used as an output pin
>+ **/
>+enum dpll_pin_state {
>+	DPLL_PIN_STATE_UNSPEC,
>+	DPLL_PIN_STATE_CONNECTED,
>+	DPLL_PIN_STATE_DISCONNECTED,
>+	DPLL_PIN_STATE_SOURCE,
>+	DPLL_PIN_STATE_OUTPUT,

The pin can be "connected" and "source" at the same time. How do you
expose that. I think we should remove "connected and just have:
	DPLL_PIN_STATE_DISCONNECTED,
	DPLL_PIN_STATE_SOURCE,
	DPLL_PIN_STATE_OUTPUT,

"state" also sound odd here, as it is not really a state. It is a "mode"
of a pin which is controlled by the user. Could you call it "MODE"?



>+
>+	__DPLL_PIN_STATE_MAX,
>+};
>+
>+#define DPLL_PIN_STATE_MAX (__DPLL_PIN_STATE_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_TYPE - pin type cahnged,
>+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
>+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
>+ * @DPLL_CHANGE_PIN_STATE - 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_TYPE,
>+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
>+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
>+	DPLL_CHANGE_PIN_STATE,
>+	DPLL_CHANGE_PIN_PRIO,

I think it is odd to have this. With every future added attribute, you
are going to add a value here as well. Basically you have 1:1
relationship.

I have to say I didn't see this concept in any other netlink usecase.
Did you?

Why exactly do you need it? Userspace usually maintains the internal
object instance anyway, it can compare incoming message with that.



>+
>+	__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_FORCED - source can be only selected by sending a request to dpll

I would probably go rather for "MODE_MANUAL". "forced" does not sound
correct there.


>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by dpll
>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode

Well, when user sets this, he basically tells the device to freerun.
What would be different between setting this and DPLL_MODE_FREERUN?


>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator

I'm clueless what this is. Isn't this Freerun too?


>+ **/
>+enum dpll_mode {
>+	DPLL_MODE_UNSPEC,
>+	DPLL_MODE_FORCED,
>+	DPLL_MODE_AUTOMATIC,
>+	DPLL_MODE_HOLDOVER,
>+	DPLL_MODE_FREERUN,
>+	DPLL_MODE_NCO,
>+
>+	__DPLL_MODE_MAX,
>+};
>+
>+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
>+
>+#endif /* _UAPI_LINUX_DPLL_H */
>-- 
>2.27.0
>

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-30 15:21   ` Jiri Pirko
@ 2022-11-30 16:23     ` Jiri Pirko
  2022-12-23 16:45     ` Kubalewski, Arkadiusz
  1 sibling, 0 replies; 87+ messages in thread
From: Jiri Pirko @ 2022-11-30 16:23 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Milena Olech, Michal Michalik

Wed, Nov 30, 2022 at 04:21:13PM CET, jiri@resnulli.us wrote:
>Tue, Nov 29, 2022 at 10:37:22PM CET, vfedorenko@novek.ru wrote:
>>From: Vadim Fedorenko <vadfed@fb.com>

[...]

>>+struct dpll_device {
>
>Just "dpll" please. Device somehow indicates this is managing device on
>the bus. It is misleading.

Hmm, on a second thought, I think it is okay to have the "device" here.
Please ignore this comment :)

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-29 21:37 ` [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions Vadim Fedorenko
  2022-11-30 15:21   ` Jiri Pirko
@ 2022-11-30 16:37   ` Jiri Pirko
  2022-12-02 11:27     ` Kubalewski, Arkadiusz
  1 sibling, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-11-30 16:37 UTC (permalink / raw)
  To: Vadim Fedorenko
  Cc: Jakub Kicinski, Arkadiusz Kubalewski, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Milena Olech, Michal Michalik

Tue, Nov 29, 2022 at 10:37:22PM CET, vfedorenko@novek.ru wrote:
>From: Vadim Fedorenko <vadfed@fb.com>

[...]

>+
>+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_DUMP_FILTER]	= { .type = NLA_U32 },
>+	[DPLLA_NETIFINDEX]	= { .type = NLA_U32 },

Only pin has a netdevice not the dpll. Also does not make sense to allow
as an input attr.


>+};
>+
>+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_TYPE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>+	[DPLLA_PIN_STATE]	= { .type = NLA_U32 },
>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>+};
>+
>+struct dpll_param {
>+	struct netlink_callback *cb;
>+	struct sk_buff *msg;
>+	struct dpll_device *dpll;
>+	struct dpll_pin *pin;
>+	enum dpll_event_change change_type;
>+};
>+
>+struct dpll_dump_ctx {
>+	int dump_filter;
>+};
>+
>+typedef int (*cb_t)(struct dpll_param *);
>+
>+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_attr *attr)
>+{
>+	enum dpll_mode m = dpll_attr_mode_get(attr);
>+
>+	if (m == DPLL_MODE_UNSPEC)
>+		return 0;
>+
>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>+}
>+
>+static int dpll_msg_add_modes_supported(struct sk_buff *msg,
>+					const struct dpll_attr *attr)
>+{
>+	enum dpll_mode i;
>+	int  ret = 0;
>+
>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>+		if (dpll_attr_mode_supported(attr, i)) {
>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>+			if (ret)
>+				return -EMSGSIZE;
>+		}
>+	}
>+
>+	return ret;
>+}
>+
>+static int dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_attr *attr)
>+{
>+	u32 source_idx;
>+
>+	if (dpll_attr_source_idx_get(attr, &source_idx))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_netifindex(struct sk_buff *msg, struct dpll_attr *attr)
>+{
>+	unsigned int netifindex; // TODO: Should be u32?
>+
>+	if (dpll_attr_netifindex_get(attr, &netifindex))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_NETIFINDEX, netifindex))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_attr *attr)
>+{
>+	enum dpll_lock_status s = dpll_attr_lock_status_get(attr);
>+
>+	if (s == DPLL_LOCK_STATUS_UNSPEC)
>+		return 0;
>+	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_attr *attr)
>+{
>+	s32 temp;
>+
>+	if (dpll_attr_temp_get(attr, &temp))
>+		return 0;
>+	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, enum dplla attr,
>+				   enum dpll_pin_type type)
>+{
>+	if (nla_put_s32(msg, attr, type))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_pin_type t = dpll_pin_attr_type_get(attr);
>+
>+	if (t == DPLL_PIN_TYPE_UNSPEC)
>+		return 0;
>+
>+	return __dpll_msg_add_pin_type(msg, DPLLA_PIN_TYPE, t);
>+}
>+
>+static int dpll_msg_add_pin_types_supported(struct sk_buff *msg,
>+					    const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_pin_type i;
>+	int ret;
>+
>+	for (i = DPLL_PIN_TYPE_UNSPEC + 1; i <= DPLL_PIN_TYPE_MAX; i++) {
>+		if (dpll_pin_attr_type_supported(attr, i)) {
>+			ret = __dpll_msg_add_pin_type(msg,
>+						      DPLLA_PIN_TYPE_SUPPORTED,
>+						      i);
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	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_pin_attr *attr)
>+{
>+	enum dpll_pin_signal_type t = dpll_pin_attr_signal_type_get(attr);
>+
>+	if (t == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>+		return 0;
>+
>+	return __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>+}
>+
>+static int
>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>+					const struct dpll_pin_attr *attr)
>+{
>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>+	enum dpll_pin_signal_type i;
>+	int ret;
>+
>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>+		if (dpll_pin_attr_signal_type_supported(attr, i)) {
>+			ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>+			if (ret)
>+				return ret;
>+		}
>+	}
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_custom_freq(struct sk_buff *msg,
>+					const struct dpll_pin_attr *attr)
>+{
>+	u32 freq;
>+
>+	if (dpll_pin_attr_custom_freq_get(attr, &freq))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_states(struct sk_buff *msg,
>+				   const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_pin_state i;
>+
>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>+		if (dpll_pin_attr_state_enabled(attr, i))
>+			if (nla_put_s32(msg, DPLLA_PIN_STATE, i))
>+				return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int dpll_msg_add_pin_states_supported(struct sk_buff *msg,
>+					     const struct dpll_pin_attr *attr)
>+{
>+	enum dpll_pin_state i;
>+
>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>+		if (dpll_pin_attr_state_supported(attr, i))
>+			if (nla_put_s32(msg, DPLLA_PIN_STATE_SUPPORTED, i))
>+				return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_pin_attr *attr)
>+{
>+	u32 prio;
>+
>+	if (dpll_pin_attr_prio_get(attr, &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_pin_attr *attr)
>+{
>+	unsigned int netifindex; // TODO: Should be u32?
>+
>+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
>+		return 0;
>+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))

I was thinking about this. It is problematic. DPLL has no notion of
network namespaces. So if the driver passes ifindex, dpll/user has no
clue in which network namespace it is (ifindexes ovelay in multiple
namespaces).

There is no easy/nice solution. For now, I would go without this and
only have linkage the opposite direction, from netdev to dpll.


>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+static int
>+dpll_msg_add_event_change_type(struct sk_buff *msg,
>+			       enum dpll_event_change event)
>+{
>+	if (nla_put_s32(msg, DPLLA_CHANGE_TYPE, event))
>+		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_attr *attr = dpll_pin_attr_alloc();
>+	struct dpll_pin *parent = NULL;
>+	int ret;
>+
>+	if (!attr)
>+		return -ENOMEM;
>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_description(msg, dpll_pin_get_description(pin));
>+	if (ret)
>+		goto out;
>+	parent = dpll_pin_get_parent(pin);
>+	if (parent) {
>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>+								    parent));
>+		if (ret)
>+			goto out;
>+	}
>+	ret = dpll_pin_get_attr(dpll, pin, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_type(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_types_supported(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_signal_type(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_signal_types_supported(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_custom_freq(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_states(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_states_supported(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_prio(msg, attr);
>+	if (ret)
>+		goto out;
>+	ret = dpll_msg_add_pin_netifindex(msg, attr);
>+	if (ret)
>+		goto out;
>+out:
>+	dpll_pin_attr_free(attr);
>+
>+	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)
>+{
>+	struct dpll_attr *attr = dpll_attr_alloc();
>+	int ret = dpll_get_attr(dpll, attr);
>+
>+	if (ret)
>+		return -EAGAIN;
>+	if (dpll_msg_add_source_pin(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_temp(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_lock_status(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_mode(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_modes_supported(msg, attr))
>+		return -EMSGSIZE;
>+	if (dpll_msg_add_netifindex(msg, attr))
>+		return -EMSGSIZE;
>+
>+	return 0;
>+}
>+
>+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_DUMP_FILTER_STATUS) {
>+		ret = __dpll_cmd_dump_status(msg, dpll);
>+		if (ret)
>+			goto out_unlock;
>+	}
>+	if (dump_filter & DPLL_DUMP_FILTER_PINS)
>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>+	dpll_unlock(dpll);
>+
>+	return ret;
>+out_unlock:
>+	dpll_unlock(dpll);
>+	return ret;
>+}
>+
>+static enum dpll_pin_type dpll_msg_read_pin_type(struct nlattr *a)
>+{
>+	return nla_get_s32(a);
>+}
>+
>+static enum dpll_pin_signal_type dpll_msg_read_pin_sig_type(struct nlattr *a)
>+{
>+	return nla_get_s32(a);
>+}
>+
>+static u32 dpll_msg_read_pin_custom_freq(struct nlattr *a)
>+{
>+	return nla_get_u32(a);
>+}
>+
>+static enum dpll_pin_state dpll_msg_read_pin_state(struct nlattr *a)
>+{
>+	return nla_get_s32(a);
>+}
>+
>+static u32 dpll_msg_read_pin_prio(struct nlattr *a)
>+{
>+	return nla_get_u32(a);
>+}
>+
>+static u32 dpll_msg_read_dump_filter(struct nlattr *a)
>+{
>+	return nla_get_u32(a);
>+}
>+
>+static int
>+dpll_pin_attr_from_nlattr(struct dpll_pin_attr *pa, struct genl_info *info)
>+{
>+	enum dpll_pin_signal_type st;
>+	enum dpll_pin_state state;
>+	enum dpll_pin_type t;
>+	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_TYPE:
>+			t = dpll_msg_read_pin_type(a);
>+			ret = dpll_pin_attr_type_set(pa, t);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_SIGNAL_TYPE:
>+			st = dpll_msg_read_pin_sig_type(a);
>+			ret = dpll_pin_attr_signal_type_set(pa, st);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_CUSTOM_FREQ:
>+			freq = dpll_msg_read_pin_custom_freq(a);
>+			ret = dpll_pin_attr_custom_freq_set(pa, freq);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_STATE:
>+			state = dpll_msg_read_pin_state(a);
>+			ret = dpll_pin_attr_state_set(pa, state);
>+			if (ret)
>+				return ret;
>+			break;
>+		case DPLLA_PIN_PRIO:
>+			prio = dpll_msg_read_pin_prio(a);
>+			ret = dpll_pin_attr_prio_set(pa, 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_pin_attr *old = NULL, *new = NULL, *delta = NULL;
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	struct nlattr **attrs = info->attrs;
>+	struct dpll_pin *pin;
>+	int ret, pin_id;
>+
>+	if (!attrs[DPLLA_PIN_IDX])
>+		return -EINVAL;
>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>+	old = dpll_pin_attr_alloc();
>+	new = dpll_pin_attr_alloc();
>+	delta = dpll_pin_attr_alloc();
>+	if (!old || !new || !delta) {
>+		ret = -ENOMEM;
>+		goto mem_free;
>+	}
>+	dpll_lock(dpll);
>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>+	if (!pin) {
>+		ret = -ENODEV;
>+		goto mem_free_unlock;
>+	}
>+	ret = dpll_pin_get_attr(dpll, pin, old);
>+	if (ret)
>+		goto mem_free_unlock;
>+	ret = dpll_pin_attr_from_nlattr(new, info);
>+	if (ret)
>+		goto mem_free_unlock;
>+	ret = dpll_pin_attr_delta(delta, new, old);
>+	dpll_unlock(dpll);
>+	if (!ret)
>+		ret = dpll_pin_set_attr(dpll, pin, delta);
>+	else
>+		ret = -EINVAL;
>+
>+	dpll_pin_attr_free(delta);
>+	dpll_pin_attr_free(new);
>+	dpll_pin_attr_free(old);
>+
>+	return ret;
>+
>+mem_free_unlock:
>+	dpll_unlock(dpll);
>+mem_free:
>+	dpll_pin_attr_free(delta);
>+	dpll_pin_attr_free(new);
>+	dpll_pin_attr_free(old);
>+	return ret;
>+}
>+
>+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_attr_from_nlattr(struct dpll_attr *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_attr_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_attr_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_attr *old = NULL, *new = NULL, *delta = NULL;
>+	struct dpll_device *dpll = info->user_ptr[0];
>+	int ret;
>+
>+	old = dpll_attr_alloc();
>+	new = dpll_attr_alloc();
>+	delta = dpll_attr_alloc();
>+	if (!old || !new || !delta) {
>+		ret = -ENOMEM;
>+		goto mem_free;
>+	}
>+	dpll_lock(dpll);
>+	ret = dpll_get_attr(dpll, old);
>+	dpll_unlock(dpll);
>+	if (!ret) {
>+		dpll_attr_from_nlattr(new, info);
>+		ret = dpll_attr_delta(delta, new, old);
>+		if (!ret)
>+			ret = dpll_set_attr(dpll, delta);
>+	}
>+
>+mem_free:
>+	dpll_attr_free(old);
>+	dpll_attr_free(new);
>+	dpll_attr_free(delta);
>+
>+	return ret;
>+}
>+
>+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_DUMP_FILTER])
>+		dump_filter =
>+			dpll_msg_read_dump_filter(attrs[DPLLA_DUMP_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_DUMP_FILTER];
>+
>+	if (attr)
>+		ctx->dump_filter = dpll_msg_read_dump_filter(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 dpll_param *p)
>+{
>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_name(p->msg, dpll_dev_name(p->dpll));
>+
>+	return ret;
>+}
>+
>+static int dpll_event_device_change(struct dpll_param *p)
>+{
>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>+
>+	if (ret)
>+		return ret;
>+	ret = dpll_msg_add_event_change_type(p->msg, p->change_type);
>+	if (ret)
>+		return ret;
>+	switch (p->change_type)	{
>+	case DPLL_CHANGE_PIN_ADD:
>+	case DPLL_CHANGE_PIN_DEL:
>+	case DPLL_CHANGE_PIN_TYPE:
>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>+	case DPLL_CHANGE_PIN_STATE:
>+	case DPLL_CHANGE_PIN_PRIO:
>+		ret = dpll_msg_add_pin_idx(p->msg, dpll_pin_idx(p->dpll, p->pin));
>+		break;
>+	default:
>+		break;
>+	}
>+
>+	return ret;
>+}
>+
>+static const cb_t event_cb[] = {
>+	[DPLL_EVENT_DEVICE_CREATE]	= dpll_event_device_id,
>+	[DPLL_EVENT_DEVICE_DELETE]	= dpll_event_device_id,
>+	[DPLL_EVENT_DEVICE_CHANGE]	= dpll_event_device_change,
>+};
>+
>+/*
>+ * Generic netlink DPLL event encoding
>+ */
>+static int dpll_send_event(enum dpll_event event, struct dpll_param *p)
>+{
>+	struct sk_buff *msg;
>+	int ret = -EMSGSIZE;
>+	void *hdr;
>+
>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+	if (!msg)
>+		return -ENOMEM;
>+	p->msg = msg;
>+
>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>+	if (!hdr)
>+		goto out_free_msg;
>+
>+	ret = event_cb[event](p);
>+	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)
>+{
>+	struct dpll_param p = { .dpll = dpll };
>+
>+	return dpll_send_event(DPLL_EVENT_DEVICE_CREATE, &p);
>+}
>+
>+int dpll_notify_device_delete(struct dpll_device *dpll)
>+{
>+	struct dpll_param p = { .dpll = dpll };
>+
>+	return dpll_send_event(DPLL_EVENT_DEVICE_DELETE, &p);
>+}
>+
>+int dpll_notify_device_change(struct dpll_device *dpll,
>+			      enum dpll_event_change event,
>+			      struct dpll_pin *pin)
>+{
>+	struct dpll_param p = { .dpll = dpll,
>+				.change_type = event,
>+				.pin = pin };

This is odd. Why don't you just pass the object you want to expose the
event for. You should have coupling between the object and send event
function:
dpll_device_notify(dpll, event);
dpll_pin_notify(pin, event);
Then you can avoid this param struct.


>+
>+	return dpll_send_event(DPLL_EVENT_DEVICE_CHANGE, &p);
>+}

[...]


>+/* 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_DUMP_FILTER - filter bitmask (int, sum of DPLL_DUMP_FILTER_* defines)
>+ * @DPLLA_NETIFINDEX - related network interface index
>+ * @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_TYPE_SUPPORTED - pin types supported (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_STATE - state of pin's capabilities (enum dpll_pin_state)
>+ * @DPLLA_PIN_STATE_SUPPORTED - available pin's capabilities
>+ *	(enum dpll_pin_state)
>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>+ * @DPLLA_CHANGE_TYPE - type of device change event
>+ *	(enum dpll_change_type)
>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>+ **/
>+enum dplla {
>+	DPLLA_UNSPEC,
>+	DPLLA_ID,
>+	DPLLA_NAME,
>+	DPLLA_MODE,
>+	DPLLA_MODE_SUPPORTED,
>+	DPLLA_SOURCE_PIN_IDX,
>+	DPLLA_LOCK_STATUS,
>+	DPLLA_TEMP,

Did you consider need for DPLLA_CLOCK_QUALITY? The our device exposes
quality of the clock. SyncE daemon needs to be aware of the clock
quality

Also, how about the clock identification. I recall this being discussed
in the past as well. This is also needed for SyncE daemon.
DPLLA_CLOCK_ID - SyncE has it at 64bit number.


>+	DPLLA_DUMP_FILTER,
>+	DPLLA_NETIFINDEX,

Duplicate, you have it under pin.


>+	DPLLA_PIN,
>+	DPLLA_PIN_IDX,
>+	DPLLA_PIN_DESCRIPTION,
>+	DPLLA_PIN_TYPE,
>+	DPLLA_PIN_TYPE_SUPPORTED,
>+	DPLLA_PIN_SIGNAL_TYPE,
>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>+	DPLLA_PIN_CUSTOM_FREQ,
>+	DPLLA_PIN_STATE,
>+	DPLLA_PIN_STATE_SUPPORTED,
>+	DPLLA_PIN_PRIO,
>+	DPLLA_PIN_PARENT_IDX,
>+	DPLLA_CHANGE_TYPE,
>+	DPLLA_PIN_NETIFINDEX,
>+	__DPLLA_MAX,
>+};
>+
>+#define DPLLA_MAX (__DPLLA_MAX - 1)


[...]


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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-11-30 12:32 ` [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Jiri Pirko
@ 2022-12-02 11:27   ` Kubalewski, Arkadiusz
  2022-12-02 16:12     ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-02 11:27 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, November 30, 2022 1:32 PM
>
>Tue, Nov 29, 2022 at 10:37:20PM CET, vfedorenko@novek.ru wrote:
>>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.
>
>Overall, I see a lot of issues on multiple levels. I will go over them in
>follow-up emails. So far, after couple of hours looking trought this, I
>have following general feelings/notes:

Hi Jiri,

As we have been participating in last version, feel obligated to answer to
the concerns.
 
>
>1) Netlink interface looks much saner than in previous versions. I will
>   send couple of notes, mainly around events and object mixtures and
>   couple of bugs/redundancies. But overall, looks fineish.
>
>2) I don't like that concept of a shared pin, at all. It makes things
>   unnecessary complicated. Just have a pin created for dpll instance
>   and that's it. If another instance has the same pin, it should create
>   it as well. Keeps things separate and easy to model. Let the
>   hw/fw/driver figure out the implementation oddities.
>   Why exactly you keep pushing the shared pin idea? Perhaps I'm missing
>   something crucial.


If the user request change on pin#0 of dpll#0, the dpll#0 knows about the
change, it reacts accordingly, and notifies the user the something has changed.
Which is rather simple.

Now, if the dpll#1 is using the same pin (pin#0 of dpll#0), the complicated
part starts. First we have to assume:
- it was initialized with the same description (as it should, to prevent
confusing the user)
- it was initialized with the same order (this is at least nice to have from
user POV, as pin indices are auto generated), and also in case of multiple pins
being shared it would be best for the user to have exactly the same number of
pins initialized, so they have same indices and initialization order doesn't
introduce additional confusion.

Thus, one reason of shared pins was to prevent having this assumptions ever.
If the pin is shared, all dplls sharing a pin would have the same description
and pin index for a shared pin out of the box.

Pin attribute changes
The change on dpll#0 pin#0 impacts also dpll#1 pin#0. Notification about the
change shall be also requested from the driver that handles dpll#1. In such
case the driver has to have some dpll monitoring/notifying mechanics, which at
first doesn't look very hard to do, but most likely only if both dplls are
initialized and managed by a single instance of a driver/firmware.

If board has 2 dplls but each one is managed by its own firmware/driver
instance. User changes frequency of pin#0 signal, the driver of dpll#0 must
also notify driver of dpll#1 that pin#0 frequency has changed, dpll#1 reacts on
the change, notifies the user.
But this is only doable with assumption, that the board is internally capable
of such internal board level communication, which in case of separated
firmwares handling multiple dplls might not be the case, or it would require
to have some other sw component feel that gap.

For complex boards with multiple dplls/sync channels, multiple ports,
multiple firmware instances, it seems to be complicated to share a pin if
each driver would have own copy and should notify all the other about changes.

To summarize, that is certainly true, shared pins idea complicates stuff
inside of dpll subsystem.
But at the same time it removes complexity from all the drivers which would use
it and is easier for the userspace due to common identification of pins.
This solution scales up without any additional complexity in the driver,
and without any need for internal per-board communication channels.

Not sure if this is good or bad, but with current version, both approaches are
possible, so it pretty much depending on the driver to initialize dplls with
separated pin objects as you have suggested (and take its complexity into
driver) or just share them.

>
>3) I don't like the concept of muxed pins and hierarchies of pins. Why
>   does user care? If pin is muxed, the rest of the pins related to this
>   one should be in state disabled/disconnected. The user only cares
>   about to see which pins are related to each other. It can be easily
>   exposed by "muxid" like this:
>   pin 1
>   pin 2
>   pin 3 muxid 100
>   pin 4 muxid 100
>   pin 5 muxid 101
>   pin 6 muxid 101
>   In this example pins 3,4 and 5,6 are muxed, therefore the user knows
>   if he connects one, the other one gets disconnected (or will have to
>   disconnect the first one explicitly first).
>

Currently DPLLA_PIN_PARENT_IDX is doing the same thing as you described, it
groups MUXed pins, the parent pin index here was most straightforward to me,
as in case of DPLL_MODE_AUTOMATIC, where dpll auto-selects highest priority
available signal. The priority can be only assigned to the pins directly
connected to the dpll. The rest of pins (which would have present
attribute DPLLA_PIN_PARENT_IDX) are the ones that require manual selection
even if DPLL_MODE_AUTOMATIC is enabled.

Enabling a particular pin and sub-pin in DPLL_MODE_AUTOMATIC requires from user
to select proper priority on on a dpll-level MUX-pin and manually select one of
the sub-pins.  
On the other hand for DPLL_MODE_FORCED, this might be also beneficial, as the
user could select a directly connected pin and muxed pin with two separated
commands, which could be handled in separated driver instances (if HW design
requires such approach) or either it can be handled just by one select call
for the pin connected directly and handled entirely in the one driver instance.

>4) I don't like the "attr" indirection. It makes things very tangled. It
>   comes from the concepts of classes and objects and takes it to
>   extreme. Not really something we are commonly used to in kernel.
>   Also, it brings no value from what I can see, only makes things very
>   hard to read and follow.
>

Yet again, true, I haven't find anything similar in the kernel, it was more
like a try to find out a way to have a single structure with all the stuff that
is passed between netlink/core/driver parts. Came up with this, and to be
honest it suits pretty well, those are well defined containers. They store
attributes that either user or driver have set, with ability to obtain a valid
value only if it was set. Thus whoever reads a struct, knows which of those
attributes were actually set.
As you said, seems a bit revolutionary, but IMHO it simplifies stuff, and
basically it is value and validity bit, which I believe is rather common in the
kernel, this differs only with the fact it is encapsulated. No direct access to
the fields of structure is available for the users.
Most probably there are some things that could be improved with it, but in
general it is very easy to use and understand how it works.
What could be improved:
- naming scheme as function names are a bit long right now, although mostly
still fits the line-char limits, thus not that problematic
- bit mask values are capable of storing 32 bits and bit(0) is always used as
unspec, which ends up with 31 values available for the enums so if by any
chance one of the attribute enums would go over 32 it could be an issue.
 
It is especially useful for multiple values passed with the same netlink
attribute id. I.e. please take a look at dpll_msg_add_pin_types_supported(..)
function.

>   Please keep things direct and simple:
>   * If some option could be changed for a pin or dpll, just have an
>     op that is directly called from netlink handler to change it.
>     There should be clear set of ops for configuration of pin and
>     dpll object. This "attr" indirection make this totally invisible.

In last review you have asked to have rather only set and get ops defined
with a single attribute struct. This is exactly that, altough encapsulated.

>   * If some attribute is const during dpll or pin lifetime, have it
>     passed during dpll or pin creation.
>
>

Only driver knows which attributes are const and which are not, this shall
be also part of driver implementation.
As I understand all the fields present in (dpll/dpll_pin)_attr, used in get/set
ops, could be altered in run-time depending on HW design.

Thanks,
Arkadiusz

>
>>
>>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 (1):
>>  dpll: add dpll_attr/dpll_pin_attr helper classes
>>
>>Vadim Fedorenko (3):
>>  dpll: Add DPLL framework base functions
>>  dpll: documentation on DPLL subsystem interface
>>  ptp_ocp: implement DPLL ops
>>
>> Documentation/networking/dpll.rst  | 271 ++++++++
>> Documentation/networking/index.rst |   1 +
>> MAINTAINERS                        |   8 +
>> drivers/Kconfig                    |   2 +
>> drivers/Makefile                   |   1 +
>> drivers/dpll/Kconfig               |   7 +
>> drivers/dpll/Makefile              |  11 +
>> drivers/dpll/dpll_attr.c           | 278 +++++++++
>> drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
>> drivers/dpll/dpll_core.h           | 176 ++++++
>> drivers/dpll/dpll_netlink.c        | 963 +++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h        |  24 +
>> drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
>> drivers/ptp/Kconfig                |   1 +
>> drivers/ptp/ptp_ocp.c              | 123 ++--
>> include/linux/dpll.h               | 261 ++++++++
>> include/linux/dpll_attr.h          | 433 +++++++++++++
>> include/uapi/linux/dpll.h          | 263 ++++++++
>> 18 files changed, 4002 insertions(+), 37 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_attr.c 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/dpll/dpll_pin_attr.c create mode 100644 include/linux/dpll.h
>> create mode 100644 include/linux/dpll_attr.h create mode 100644
>> include/uapi/linux/dpll.h
>>
>>--
>>2.27.0
>>

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

* RE: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-11-30 12:41   ` Jiri Pirko
@ 2022-12-02 11:27     ` Kubalewski, Arkadiusz
  2022-12-02 12:48       ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-02 11:27 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	Vadim Fedorenko, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, November 30, 2022 1:41 PM
>
>Tue, Nov 29, 2022 at 10:37:24PM CET, vfedorenko@novek.ru wrote:
>>From: Vadim Fedorenko <vadfed@fb.com>
>>
>>Implement basic DPLL operations in ptp_ocp driver as the
>>simplest example of using new subsystem.
>>
>>Signed-off-by: Vadim Fedorenko <vadfed@fb.com>
>>---
>> drivers/ptp/Kconfig   |   1 +
>> drivers/ptp/ptp_ocp.c | 123 +++++++++++++++++++++++++++++-------------
>> 2 files changed, 87 insertions(+), 37 deletions(-)
>>
>>diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>>index fe4971b65c64..8c4cfabc1bfa 100644
>>--- a/drivers/ptp/Kconfig
>>+++ b/drivers/ptp/Kconfig
>>@@ -177,6 +177,7 @@ config PTP_1588_CLOCK_OCP
>> 	depends on COMMON_CLK
>> 	select NET_DEVLINK
>> 	select CRC16
>>+	select DPLL
>> 	help
>> 	  This driver adds support for an OpenCompute time card.
>>
>>diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
>>index 154d58cbd9ce..605853ac4a12 100644
>>--- a/drivers/ptp/ptp_ocp.c
>>+++ b/drivers/ptp/ptp_ocp.c
>>@@ -23,6 +23,8 @@
>> #include <linux/mtd/mtd.h>
>> #include <linux/nvmem-consumer.h>
>> #include <linux/crc16.h>
>>+#include <linux/dpll.h>
>>+#include <uapi/linux/dpll.h>
>>
>> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>>@@ -353,6 +355,7 @@ struct ptp_ocp {
>> 	struct ptp_ocp_signal	signal[4];
>> 	struct ptp_ocp_sma_connector sma[4];
>> 	const struct ocp_sma_op *sma_op;
>>+	struct dpll_device *dpll;
>> };
>>
>> #define OCP_REQ_TIMESTAMP	BIT(0)
>>@@ -835,18 +838,19 @@ static DEFINE_IDR(ptp_ocp_idr);
>> struct ocp_selector {
>> 	const char *name;
>> 	int value;
>>+	int dpll_type;
>> };
>>
>> static const struct ocp_selector ptp_ocp_clock[] = {
>>-	{ .name = "NONE",	.value = 0 },
>>-	{ .name = "TOD",	.value = 1 },
>>-	{ .name = "IRIG",	.value = 2 },
>>-	{ .name = "PPS",	.value = 3 },
>>-	{ .name = "PTP",	.value = 4 },
>>-	{ .name = "RTC",	.value = 5 },
>>-	{ .name = "DCF",	.value = 6 },
>>-	{ .name = "REGS",	.value = 0xfe },
>>-	{ .name = "EXT",	.value = 0xff },
>>+	{ .name = "NONE",	.value = 0,		.dpll_type = 0 },
>>+	{ .name = "TOD",	.value = 1,		.dpll_type = 0 },
>>+	{ .name = "IRIG",	.value = 2,		.dpll_type = 0 },
>>+	{ .name = "PPS",	.value = 3,		.dpll_type = 0 },
>>+	{ .name = "PTP",	.value = 4,		.dpll_type = 0 },
>>+	{ .name = "RTC",	.value = 5,		.dpll_type = 0 },
>>+	{ .name = "DCF",	.value = 6,		.dpll_type = 0 },
>>+	{ .name = "REGS",	.value = 0xfe,		.dpll_type = 0 },
>>+	{ .name = "EXT",	.value = 0xff,		.dpll_type = 0 },
>> 	{ }
>> };
>>
>>@@ -855,37 +859,37 @@ static const struct ocp_selector ptp_ocp_clock[] = {
>> #define SMA_SELECT_MASK		GENMASK(14, 0)
>>
>> static const struct ocp_selector ptp_ocp_sma_in[] = {
>>-	{ .name = "10Mhz",	.value = 0x0000 },
>>-	{ .name = "PPS1",	.value = 0x0001 },
>>-	{ .name = "PPS2",	.value = 0x0002 },
>>-	{ .name = "TS1",	.value = 0x0004 },
>>-	{ .name = "TS2",	.value = 0x0008 },
>>-	{ .name = "IRIG",	.value = 0x0010 },
>>-	{ .name = "DCF",	.value = 0x0020 },
>>-	{ .name = "TS3",	.value = 0x0040 },
>>-	{ .name = "TS4",	.value = 0x0080 },
>>-	{ .name = "FREQ1",	.value = 0x0100 },
>>-	{ .name = "FREQ2",	.value = 0x0200 },
>>-	{ .name = "FREQ3",	.value = 0x0400 },
>>-	{ .name = "FREQ4",	.value = 0x0800 },
>>-	{ .name = "None",	.value = SMA_DISABLE },
>>+	{ .name = "10Mhz",	.value = 0x0000,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_10_MHZ },
>>+	{ .name = "PPS1",	.value = 0x0001,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_1_PPS },
>>+	{ .name = "PPS2",	.value = 0x0002,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_1_PPS },
>>+	{ .name = "TS1",	.value = 0x0004,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "TS2",	.value = 0x0008,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "IRIG",	.value = 0x0010,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "DCF",	.value = 0x0020,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "TS3",	.value = 0x0040,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "TS4",	.value = 0x0080,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "FREQ1",	.value = 0x0100,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "FREQ2",	.value = 0x0200,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "FREQ3",	.value = 0x0400,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "FREQ4",	.value = 0x0800,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "None",	.value = SMA_DISABLE,	.dpll_type = 0 },
>> 	{ }
>> };
>>
>> static const struct ocp_selector ptp_ocp_sma_out[] = {
>>-	{ .name = "10Mhz",	.value = 0x0000 },
>>-	{ .name = "PHC",	.value = 0x0001 },
>>-	{ .name = "MAC",	.value = 0x0002 },
>>-	{ .name = "GNSS1",	.value = 0x0004 },
>>-	{ .name = "GNSS2",	.value = 0x0008 },
>>-	{ .name = "IRIG",	.value = 0x0010 },
>>-	{ .name = "DCF",	.value = 0x0020 },
>>-	{ .name = "GEN1",	.value = 0x0040 },
>>-	{ .name = "GEN2",	.value = 0x0080 },
>>-	{ .name = "GEN3",	.value = 0x0100 },
>>-	{ .name = "GEN4",	.value = 0x0200 },
>>-	{ .name = "GND",	.value = 0x2000 },
>>-	{ .name = "VCC",	.value = 0x4000 },
>>+	{ .name = "10Mhz",	.value = 0x0000,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_10_MHZ },
>>+	{ .name = "PHC",	.value = 0x0001,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "MAC",	.value = 0x0002,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "GNSS1",	.value = 0x0004,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_1_PPS },
>>+	{ .name = "GNSS2",	.value = 0x0008,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_1_PPS },
>>+	{ .name = "IRIG",	.value = 0x0010,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "DCF",	.value = 0x0020,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "GEN1",	.value = 0x0040,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "GEN2",	.value = 0x0080,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "GEN3",	.value = 0x0100,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "GEN4",	.value = 0x0200,	.dpll_type =
>DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ },
>>+	{ .name = "GND",	.value = 0x2000,	.dpll_type = 0 },
>>+	{ .name = "VCC",	.value = 0x4000,	.dpll_type = 0 },
>> 	{ }
>> };
>>
>>@@ -4175,12 +4179,41 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>> 	device_unregister(&bp->dev);
>> }
>>
>>+static int ptp_ocp_dpll_get_attr(struct dpll_device *dpll, struct
>dpll_attr *attr)
>>+{
>>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>>+	int sync;
>>+
>>+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>>+	dpll_attr_lock_status_set(attr, sync ? DPLL_LOCK_STATUS_LOCKED :
>DPLL_LOCK_STATUS_UNLOCKED);
>
>get,set,confuse. This attr thing sucks, sorry :/

Once again, I feel obligated to add some explanations :)

getter is ops called by dpll subsystem, it requires data, so here value shall
be set for the caller, right?
Also have explained the reason why this attr struct and functions are done this
way in the response to cover letter concerns.

>
>
>>+
>>+	return 0;
>>+}
>>+
>>+static int ptp_ocp_dpll_pin_get_attr(struct dpll_device *dpll, struct
>dpll_pin *pin,
>>+				     struct dpll_pin_attr *attr)
>>+{
>>+	dpll_pin_attr_type_set(attr, DPLL_PIN_TYPE_EXT);
>
>This is exactly what I was talking about in the cover letter. This is
>const, should be put into static struct and passed to
>dpll_device_alloc().

Actually this type or some other parameters might change in the run-time,
depends on the device, it is up to the driver how it will handle any getter,
if driver knows it won't change it could also have some static member and copy
the data with: dpll_pin_attr_copy(...);

>
>
>>+	return 0;
>>+}
>>+
>>+static struct dpll_device_ops dpll_ops = {
>>+	.get	= ptp_ocp_dpll_get_attr,
>>+};
>>+
>>+static struct dpll_pin_ops dpll_pin_ops = {
>>+	.get	= ptp_ocp_dpll_pin_get_attr,
>>+};
>>+
>> static int
>> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> {
>>+	const u8 dpll_cookie[DPLL_COOKIE_LEN] = { "OCP" };
>>+	char pin_desc[PIN_DESC_LEN];
>> 	struct devlink *devlink;
>>+	struct dpll_pin *pin;
>> 	struct ptp_ocp *bp;
>>-	int err;
>>+	int err, i;
>>
>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev-
>>dev);
>> 	if (!devlink) {
>>@@ -4230,6 +4263,20 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct
>pci_device_id *id)
>>
>> 	ptp_ocp_info(bp);
>> 	devlink_register(devlink);
>>+
>>+	bp->dpll = dpll_device_alloc(&dpll_ops, DPLL_TYPE_PPS, dpll_cookie,
>pdev->bus->number, bp, &pdev->dev);
>>+	if (!bp->dpll) {
>>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>>+		goto out;
>>+	}
>>+	dpll_device_register(bp->dpll);
>
>You still have the 2 step init process. I believe it would be better to
>just have dpll_device_create/destroy() to do it in one shot.

For me either is ok, but due to pins alloc/register as explained below I would
leave it as it is.

>
>
>>+
>>+	for (i = 0; i < 4; i++) {
>>+		snprintf(pin_desc, PIN_DESC_LEN, "sma%d", i + 1);
>>+		pin = dpll_pin_alloc(pin_desc, PIN_DESC_LEN);
>>+		dpll_pin_register(bp->dpll, pin, &dpll_pin_ops, bp);
>
>Same here, no point of having 2 step init.

The alloc of a pin is not required if the pin already exist and would be just
registered with another dpll.
Once we decide to entirely drop shared pins idea this could be probably done,
although other kernel code usually use this twostep approach?

>
>
>>+	}
>>+
>> 	return 0;
>
>
>Btw, did you consider having dpll instance here as and auxdev? It would
>be suitable I believe. It is quite simple to do it. See following patch
>as an example:

I haven't think about it, definetly gonna take a look to see if there any
benefits in ice.

Thanks,
Arkadiusz

>
>commit bd02fd76d1909637c95e8ef13e7fd1e748af910d
>Author: Jiri Pirko <jiri@nvidia.com>
>Date:   Mon Jul 25 10:29:17 2022 +0200
>
>    mlxsw: core_linecards: Introduce per line card auxiliary device
>
>
>
>
>>
>> out:
>>@@ -4247,6 +4294,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>> 	struct devlink *devlink = priv_to_devlink(bp);
>>
>>+	dpll_device_unregister(bp->dpll);
>>+	dpll_device_free(bp->dpll);
>> 	devlink_unregister(devlink);
>> 	ptp_ocp_detach(bp);
>> 	pci_disable_device(pdev);
>>--
>>2.27.0
>>

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

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-30 16:37   ` Jiri Pirko
@ 2022-12-02 11:27     ` Kubalewski, Arkadiusz
  2022-12-02 12:39       ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-02 11:27 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	Vadim Fedorenko, linux-arm-kernel, linux-clk, Olech, Milena,
	Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, November 30, 2022 5:37 PM
>Tue, Nov 29, 2022 at 10:37:22PM CET, vfedorenko@novek.ru wrote:
>>From: Vadim Fedorenko <vadfed@fb.com>
>
>[...]
>
>>+
>>+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_DUMP_FILTER]	= { .type = NLA_U32 },
>>+	[DPLLA_NETIFINDEX]	= { .type = NLA_U32 },
>
>Only pin has a netdevice not the dpll. Also does not make sense to allow
>as an input attr.

Yes, this part shall be removed.

>
>
>>+};
>>+
>>+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_TYPE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>>+	[DPLLA_PIN_STATE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>>+};
>>+
>>+struct dpll_param {
>>+	struct netlink_callback *cb;
>>+	struct sk_buff *msg;
>>+	struct dpll_device *dpll;
>>+	struct dpll_pin *pin;
>>+	enum dpll_event_change change_type;
>>+};
>>+
>>+struct dpll_dump_ctx {
>>+	int dump_filter;
>>+};
>>+
>>+typedef int (*cb_t)(struct dpll_param *);
>>+
>>+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_attr
>*attr)
>>+{
>>+	enum dpll_mode m = dpll_attr_mode_get(attr);
>>+
>>+	if (m == DPLL_MODE_UNSPEC)
>>+		return 0;
>>+
>>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>>+}
>>+
>>+static int dpll_msg_add_modes_supported(struct sk_buff *msg,
>>+					const struct dpll_attr *attr)
>>+{
>>+	enum dpll_mode i;
>>+	int  ret = 0;
>>+
>>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>>+		if (dpll_attr_mode_supported(attr, i)) {
>>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>>+			if (ret)
>>+				return -EMSGSIZE;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_attr
>*attr)
>>+{
>>+	u32 source_idx;
>>+
>>+	if (dpll_attr_source_idx_get(attr, &source_idx))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_netifindex(struct sk_buff *msg, struct dpll_attr
>*attr)
>>+{
>>+	unsigned int netifindex; // TODO: Should be u32?
>>+
>>+	if (dpll_attr_netifindex_get(attr, &netifindex))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_NETIFINDEX, netifindex))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_attr
>*attr)
>>+{
>>+	enum dpll_lock_status s = dpll_attr_lock_status_get(attr);
>>+
>>+	if (s == DPLL_LOCK_STATUS_UNSPEC)
>>+		return 0;
>>+	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_attr *attr)
>>+{
>>+	s32 temp;
>>+
>>+	if (dpll_attr_temp_get(attr, &temp))
>>+		return 0;
>>+	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, enum dplla attr,
>>+				   enum dpll_pin_type type)
>>+{
>>+	if (nla_put_s32(msg, attr, type))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_pin_attr
>*attr)
>>+{
>>+	enum dpll_pin_type t = dpll_pin_attr_type_get(attr);
>>+
>>+	if (t == DPLL_PIN_TYPE_UNSPEC)
>>+		return 0;
>>+
>>+	return __dpll_msg_add_pin_type(msg, DPLLA_PIN_TYPE, t);
>>+}
>>+
>>+static int dpll_msg_add_pin_types_supported(struct sk_buff *msg,
>>+					    const struct dpll_pin_attr *attr)
>>+{
>>+	enum dpll_pin_type i;
>>+	int ret;
>>+
>>+	for (i = DPLL_PIN_TYPE_UNSPEC + 1; i <= DPLL_PIN_TYPE_MAX; i++) {
>>+		if (dpll_pin_attr_type_supported(attr, i)) {
>>+			ret = __dpll_msg_add_pin_type(msg,
>>+						      DPLLA_PIN_TYPE_SUPPORTED,
>>+						      i);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	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_pin_attr *attr)
>>+{
>>+	enum dpll_pin_signal_type t = dpll_pin_attr_signal_type_get(attr);
>>+
>>+	if (t == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>>+		return 0;
>>+
>>+	return __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>>+					const struct dpll_pin_attr *attr)
>>+{
>>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>>+	enum dpll_pin_signal_type i;
>>+	int ret;
>>+
>>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>>+		if (dpll_pin_attr_signal_type_supported(attr, i)) {
>>+			ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_custom_freq(struct sk_buff *msg,
>>+					const struct dpll_pin_attr *attr)
>>+{
>>+	u32 freq;
>>+
>>+	if (dpll_pin_attr_custom_freq_get(attr, &freq))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_states(struct sk_buff *msg,
>>+				   const struct dpll_pin_attr *attr)
>>+{
>>+	enum dpll_pin_state i;
>>+
>>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>>+		if (dpll_pin_attr_state_enabled(attr, i))
>>+			if (nla_put_s32(msg, DPLLA_PIN_STATE, i))
>>+				return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_states_supported(struct sk_buff *msg,
>>+					     const struct dpll_pin_attr *attr)
>>+{
>>+	enum dpll_pin_state i;
>>+
>>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>>+		if (dpll_pin_attr_state_supported(attr, i))
>>+			if (nla_put_s32(msg, DPLLA_PIN_STATE_SUPPORTED, i))
>>+				return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_pin_attr
>*attr)
>>+{
>>+	u32 prio;
>>+
>>+	if (dpll_pin_attr_prio_get(attr, &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_pin_attr *attr)
>>+{
>>+	unsigned int netifindex; // TODO: Should be u32?
>>+
>>+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>
>I was thinking about this. It is problematic. DPLL has no notion of
>network namespaces. So if the driver passes ifindex, dpll/user has no
>clue in which network namespace it is (ifindexes ovelay in multiple
>namespaces).
>
>There is no easy/nice solution. For now, I would go without this and
>only have linkage the opposite direction, from netdev to dpll.

Well, makes sense to me.
Although as I have checked `ip a` showed the same ifindex either if port was
in the namespace or not.
Isn't it better to let the user know ifindex, even if he has to iterate all
the namespaces he has created?

>
>
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_event_change_type(struct sk_buff *msg,
>>+			       enum dpll_event_change event)
>>+{
>>+	if (nla_put_s32(msg, DPLLA_CHANGE_TYPE, event))
>>+		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_attr *attr = dpll_pin_attr_alloc();
>>+	struct dpll_pin *parent = NULL;
>>+	int ret;
>>+
>>+	if (!attr)
>>+		return -ENOMEM;
>>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_description(msg,
>dpll_pin_get_description(pin));
>>+	if (ret)
>>+		goto out;
>>+	parent = dpll_pin_get_parent(pin);
>>+	if (parent) {
>>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>>+								    parent));
>>+		if (ret)
>>+			goto out;
>>+	}
>>+	ret = dpll_pin_get_attr(dpll, pin, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_type(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_types_supported(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_signal_type(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_signal_types_supported(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_custom_freq(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_states(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_states_supported(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_prio(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_netifindex(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+out:
>>+	dpll_pin_attr_free(attr);
>>+
>>+	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)
>>+{
>>+	struct dpll_attr *attr = dpll_attr_alloc();
>>+	int ret = dpll_get_attr(dpll, attr);
>>+
>>+	if (ret)
>>+		return -EAGAIN;
>>+	if (dpll_msg_add_source_pin(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_temp(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_lock_status(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_mode(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_modes_supported(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_netifindex(msg, attr))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+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_DUMP_FILTER_STATUS) {
>>+		ret = __dpll_cmd_dump_status(msg, dpll);
>>+		if (ret)
>>+			goto out_unlock;
>>+	}
>>+	if (dump_filter & DPLL_DUMP_FILTER_PINS)
>>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>>+	dpll_unlock(dpll);
>>+
>>+	return ret;
>>+out_unlock:
>>+	dpll_unlock(dpll);
>>+	return ret;
>>+}
>>+
>>+static enum dpll_pin_type dpll_msg_read_pin_type(struct nlattr *a)
>>+{
>>+	return nla_get_s32(a);
>>+}
>>+
>>+static enum dpll_pin_signal_type dpll_msg_read_pin_sig_type(struct nlattr
>*a)
>>+{
>>+	return nla_get_s32(a);
>>+}
>>+
>>+static u32 dpll_msg_read_pin_custom_freq(struct nlattr *a)
>>+{
>>+	return nla_get_u32(a);
>>+}
>>+
>>+static enum dpll_pin_state dpll_msg_read_pin_state(struct nlattr *a)
>>+{
>>+	return nla_get_s32(a);
>>+}
>>+
>>+static u32 dpll_msg_read_pin_prio(struct nlattr *a)
>>+{
>>+	return nla_get_u32(a);
>>+}
>>+
>>+static u32 dpll_msg_read_dump_filter(struct nlattr *a)
>>+{
>>+	return nla_get_u32(a);
>>+}
>>+
>>+static int
>>+dpll_pin_attr_from_nlattr(struct dpll_pin_attr *pa, struct genl_info
>*info)
>>+{
>>+	enum dpll_pin_signal_type st;
>>+	enum dpll_pin_state state;
>>+	enum dpll_pin_type t;
>>+	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_TYPE:
>>+			t = dpll_msg_read_pin_type(a);
>>+			ret = dpll_pin_attr_type_set(pa, t);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_SIGNAL_TYPE:
>>+			st = dpll_msg_read_pin_sig_type(a);
>>+			ret = dpll_pin_attr_signal_type_set(pa, st);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_CUSTOM_FREQ:
>>+			freq = dpll_msg_read_pin_custom_freq(a);
>>+			ret = dpll_pin_attr_custom_freq_set(pa, freq);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_STATE:
>>+			state = dpll_msg_read_pin_state(a);
>>+			ret = dpll_pin_attr_state_set(pa, state);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_PRIO:
>>+			prio = dpll_msg_read_pin_prio(a);
>>+			ret = dpll_pin_attr_prio_set(pa, 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_pin_attr *old = NULL, *new = NULL, *delta = NULL;
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	struct nlattr **attrs = info->attrs;
>>+	struct dpll_pin *pin;
>>+	int ret, pin_id;
>>+
>>+	if (!attrs[DPLLA_PIN_IDX])
>>+		return -EINVAL;
>>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>>+	old = dpll_pin_attr_alloc();
>>+	new = dpll_pin_attr_alloc();
>>+	delta = dpll_pin_attr_alloc();
>>+	if (!old || !new || !delta) {
>>+		ret = -ENOMEM;
>>+		goto mem_free;
>>+	}
>>+	dpll_lock(dpll);
>>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>>+	if (!pin) {
>>+		ret = -ENODEV;
>>+		goto mem_free_unlock;
>>+	}
>>+	ret = dpll_pin_get_attr(dpll, pin, old);
>>+	if (ret)
>>+		goto mem_free_unlock;
>>+	ret = dpll_pin_attr_from_nlattr(new, info);
>>+	if (ret)
>>+		goto mem_free_unlock;
>>+	ret = dpll_pin_attr_delta(delta, new, old);
>>+	dpll_unlock(dpll);
>>+	if (!ret)
>>+		ret = dpll_pin_set_attr(dpll, pin, delta);
>>+	else
>>+		ret = -EINVAL;
>>+
>>+	dpll_pin_attr_free(delta);
>>+	dpll_pin_attr_free(new);
>>+	dpll_pin_attr_free(old);
>>+
>>+	return ret;
>>+
>>+mem_free_unlock:
>>+	dpll_unlock(dpll);
>>+mem_free:
>>+	dpll_pin_attr_free(delta);
>>+	dpll_pin_attr_free(new);
>>+	dpll_pin_attr_free(old);
>>+	return ret;
>>+}
>>+
>>+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_attr_from_nlattr(struct dpll_attr *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_attr_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_attr_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_attr *old = NULL, *new = NULL, *delta = NULL;
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	int ret;
>>+
>>+	old = dpll_attr_alloc();
>>+	new = dpll_attr_alloc();
>>+	delta = dpll_attr_alloc();
>>+	if (!old || !new || !delta) {
>>+		ret = -ENOMEM;
>>+		goto mem_free;
>>+	}
>>+	dpll_lock(dpll);
>>+	ret = dpll_get_attr(dpll, old);
>>+	dpll_unlock(dpll);
>>+	if (!ret) {
>>+		dpll_attr_from_nlattr(new, info);
>>+		ret = dpll_attr_delta(delta, new, old);
>>+		if (!ret)
>>+			ret = dpll_set_attr(dpll, delta);
>>+	}
>>+
>>+mem_free:
>>+	dpll_attr_free(old);
>>+	dpll_attr_free(new);
>>+	dpll_attr_free(delta);
>>+
>>+	return ret;
>>+}
>>+
>>+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_DUMP_FILTER])
>>+		dump_filter =
>>+			dpll_msg_read_dump_filter(attrs[DPLLA_DUMP_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_DUMP_FILTER];
>>+
>>+	if (attr)
>>+		ctx->dump_filter = dpll_msg_read_dump_filter(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 dpll_param *p)
>>+{
>>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_name(p->msg, dpll_dev_name(p->dpll));
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_event_device_change(struct dpll_param *p)
>>+{
>>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_event_change_type(p->msg, p->change_type);
>>+	if (ret)
>>+		return ret;
>>+	switch (p->change_type)	{
>>+	case DPLL_CHANGE_PIN_ADD:
>>+	case DPLL_CHANGE_PIN_DEL:
>>+	case DPLL_CHANGE_PIN_TYPE:
>>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>>+	case DPLL_CHANGE_PIN_STATE:
>>+	case DPLL_CHANGE_PIN_PRIO:
>>+		ret = dpll_msg_add_pin_idx(p->msg, dpll_pin_idx(p->dpll, p-
>>pin));
>>+		break;
>>+	default:
>>+		break;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static const cb_t event_cb[] = {
>>+	[DPLL_EVENT_DEVICE_CREATE]	= dpll_event_device_id,
>>+	[DPLL_EVENT_DEVICE_DELETE]	= dpll_event_device_id,
>>+	[DPLL_EVENT_DEVICE_CHANGE]	= dpll_event_device_change,
>>+};
>>+
>>+/*
>>+ * Generic netlink DPLL event encoding
>>+ */
>>+static int dpll_send_event(enum dpll_event event, struct dpll_param *p)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+	p->msg = msg;
>>+
>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>>+	if (!hdr)
>>+		goto out_free_msg;
>>+
>>+	ret = event_cb[event](p);
>>+	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)
>>+{
>>+	struct dpll_param p = { .dpll = dpll };
>>+
>>+	return dpll_send_event(DPLL_EVENT_DEVICE_CREATE, &p);
>>+}
>>+
>>+int dpll_notify_device_delete(struct dpll_device *dpll)
>>+{
>>+	struct dpll_param p = { .dpll = dpll };
>>+
>>+	return dpll_send_event(DPLL_EVENT_DEVICE_DELETE, &p);
>>+}
>>+
>>+int dpll_notify_device_change(struct dpll_device *dpll,
>>+			      enum dpll_event_change event,
>>+			      struct dpll_pin *pin)
>>+{
>>+	struct dpll_param p = { .dpll = dpll,
>>+				.change_type = event,
>>+				.pin = pin };
>
>This is odd. Why don't you just pass the object you want to expose the
>event for. You should have coupling between the object and send event
>function:
>dpll_device_notify(dpll, event);
>dpll_pin_notify(pin, event);
>Then you can avoid this param struct.

Makes sense to me.

>
>
>>+
>>+	return dpll_send_event(DPLL_EVENT_DEVICE_CHANGE, &p);
>>+}
>
>[...]
>
>
>>+/* 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_DUMP_FILTER - filter bitmask (int, sum of DPLL_DUMP_FILTER_*
>defines)
>>+ * @DPLLA_NETIFINDEX - related network interface index
>>+ * @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_TYPE_SUPPORTED - pin types supported (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_STATE - state of pin's capabilities (enum dpll_pin_state)
>>+ * @DPLLA_PIN_STATE_SUPPORTED - available pin's capabilities
>>+ *	(enum dpll_pin_state)
>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>+ *	(enum dpll_change_type)
>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>+ **/
>>+enum dplla {
>>+	DPLLA_UNSPEC,
>>+	DPLLA_ID,
>>+	DPLLA_NAME,
>>+	DPLLA_MODE,
>>+	DPLLA_MODE_SUPPORTED,
>>+	DPLLA_SOURCE_PIN_IDX,
>>+	DPLLA_LOCK_STATUS,
>>+	DPLLA_TEMP,
>
>Did you consider need for DPLLA_CLOCK_QUALITY? The our device exposes
>quality of the clock. SyncE daemon needs to be aware of the clock
>quality
>
>Also, how about the clock identification. I recall this being discussed
>in the past as well. This is also needed for SyncE daemon.
>DPLLA_CLOCK_ID - SyncE has it at 64bit number.
>

Yep, definitely I agree with both.

>
>>+	DPLLA_DUMP_FILTER,
>>+	DPLLA_NETIFINDEX,
>
>Duplicate, you have it under pin.

The pin can have netifindex as pin signal source may originate there by
Clock recovery mechanics.
The dpll can have ifindex as it "owns" the dpll.
Shall user know about it? probably nothing usefull for him, although
didn't Maciej Machnikowski asked to have such traceability?

Thanks,
Arkadiusz

>
>
>>+	DPLLA_PIN,
>>+	DPLLA_PIN_IDX,
>>+	DPLLA_PIN_DESCRIPTION,
>>+	DPLLA_PIN_TYPE,
>>+	DPLLA_PIN_TYPE_SUPPORTED,
>>+	DPLLA_PIN_SIGNAL_TYPE,
>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>+	DPLLA_PIN_CUSTOM_FREQ,
>>+	DPLLA_PIN_STATE,
>>+	DPLLA_PIN_STATE_SUPPORTED,
>>+	DPLLA_PIN_PRIO,
>>+	DPLLA_PIN_PARENT_IDX,
>>+	DPLLA_CHANGE_TYPE,
>>+	DPLLA_PIN_NETIFINDEX,
>>+	__DPLLA_MAX,
>>+};
>>+
>>+#define DPLLA_MAX (__DPLLA_MAX - 1)
>
>
>[...]


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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-02 11:27     ` Kubalewski, Arkadiusz
@ 2022-12-02 12:39       ` Jiri Pirko
  2022-12-02 14:54         ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-02 12:39 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk, Olech,
	Milena, Michalik, Michal

Fri, Dec 02, 2022 at 12:27:35PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, November 30, 2022 5:37 PM
>>Tue, Nov 29, 2022 at 10:37:22PM CET, vfedorenko@novek.ru wrote:
>>>From: Vadim Fedorenko <vadfed@fb.com>

[...]


>>>+static int
>>>+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct
>>dpll_pin_attr *attr)
>>>+{
>>>+	unsigned int netifindex; // TODO: Should be u32?
>>>+
>>>+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
>>>+		return 0;
>>>+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>>
>>I was thinking about this. It is problematic. DPLL has no notion of
>>network namespaces. So if the driver passes ifindex, dpll/user has no
>>clue in which network namespace it is (ifindexes ovelay in multiple
>>namespaces).
>>
>>There is no easy/nice solution. For now, I would go without this and
>>only have linkage the opposite direction, from netdev to dpll.
>
>Well, makes sense to me.
>Although as I have checked `ip a` showed the same ifindex either if port was
>in the namespace or not.

That is not the problem. The problem is, that you can have following
two netdevs with the same ifindex each in different netns.
1) netdev x: ifindex 8, netns ns1
2) netdev y: ifindex 8, netns ns2

>Isn't it better to let the user know ifindex, even if he has to iterate all
>the namespaces he has created?

Definitelly not. As I showed above, one ifindex may refer to multiple
netdevice instances.


[...]


>>>+	DPLLA_NETIFINDEX,
>>
>>Duplicate, you have it under pin.
>
>The pin can have netifindex as pin signal source may originate there by
>Clock recovery mechanics.
>The dpll can have ifindex as it "owns" the dpll.

DPLL is not owned by any netdevice. That does not make any sense.
Netdevice may be "child" of the same PCI device as the dpll instance.
But that's it.


>Shall user know about it? probably nothing usefull for him, although
>didn't Maciej Machnikowski asked to have such traceability?

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-02 11:27     ` Kubalewski, Arkadiusz
@ 2022-12-02 12:48       ` Jiri Pirko
  2022-12-02 14:39         ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-02 12:48 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

Fri, Dec 02, 2022 at 12:27:32PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, November 30, 2022 1:41 PM
>>
>>Tue, Nov 29, 2022 at 10:37:24PM CET, vfedorenko@novek.ru wrote:
>>>From: Vadim Fedorenko <vadfed@fb.com>

[...]


>>>+static int ptp_ocp_dpll_get_attr(struct dpll_device *dpll, struct
>>dpll_attr *attr)
>>>+{
>>>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>>>+	int sync;
>>>+
>>>+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>>>+	dpll_attr_lock_status_set(attr, sync ? DPLL_LOCK_STATUS_LOCKED :
>>DPLL_LOCK_STATUS_UNLOCKED);
>>
>>get,set,confuse. This attr thing sucks, sorry :/
>
>Once again, I feel obligated to add some explanations :)
>
>getter is ops called by dpll subsystem, it requires data, so here value shall
>be set for the caller, right?
>Also have explained the reason why this attr struct and functions are done this
>way in the response to cover letter concerns.

Okay, I will react there.


>
>>
>>
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int ptp_ocp_dpll_pin_get_attr(struct dpll_device *dpll, struct
>>dpll_pin *pin,
>>>+				     struct dpll_pin_attr *attr)
>>>+{
>>>+	dpll_pin_attr_type_set(attr, DPLL_PIN_TYPE_EXT);
>>
>>This is exactly what I was talking about in the cover letter. This is
>>const, should be put into static struct and passed to
>>dpll_device_alloc().
>
>Actually this type or some other parameters might change in the run-time,

No. This should not change.
If the pin is SyncE port, it's that for all lifetime of pin. It cannot
turn to be a EXT/SMA connector all of the sudden. This should be
definitelly fixed, it's a device topology.

Can you explain the exact scenario when the change of personality of pin
can happen? Perhaps I'm missing something.



>depends on the device, it is up to the driver how it will handle any getter,
>if driver knows it won't change it could also have some static member and copy
>the data with: dpll_pin_attr_copy(...);
>
>>
>>
>>>+	return 0;
>>>+}
>>>+
>>>+static struct dpll_device_ops dpll_ops = {
>>>+	.get	= ptp_ocp_dpll_get_attr,
>>>+};
>>>+
>>>+static struct dpll_pin_ops dpll_pin_ops = {
>>>+	.get	= ptp_ocp_dpll_pin_get_attr,
>>>+};
>>>+
>>> static int
>>> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>>> {
>>>+	const u8 dpll_cookie[DPLL_COOKIE_LEN] = { "OCP" };
>>>+	char pin_desc[PIN_DESC_LEN];
>>> 	struct devlink *devlink;
>>>+	struct dpll_pin *pin;
>>> 	struct ptp_ocp *bp;
>>>-	int err;
>>>+	int err, i;
>>>
>>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev-
>>>dev);
>>> 	if (!devlink) {
>>>@@ -4230,6 +4263,20 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct
>>pci_device_id *id)
>>>
>>> 	ptp_ocp_info(bp);
>>> 	devlink_register(devlink);
>>>+
>>>+	bp->dpll = dpll_device_alloc(&dpll_ops, DPLL_TYPE_PPS, dpll_cookie,
>>pdev->bus->number, bp, &pdev->dev);
>>>+	if (!bp->dpll) {
>>>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>>>+		goto out;
>>>+	}
>>>+	dpll_device_register(bp->dpll);
>>
>>You still have the 2 step init process. I believe it would be better to
>>just have dpll_device_create/destroy() to do it in one shot.
>
>For me either is ok, but due to pins alloc/register as explained below I would
>leave it as it is.

Please don't, it has no value. Just adds unnecesary code. Have it nice
and simple.


>
>>
>>
>>>+
>>>+	for (i = 0; i < 4; i++) {
>>>+		snprintf(pin_desc, PIN_DESC_LEN, "sma%d", i + 1);
>>>+		pin = dpll_pin_alloc(pin_desc, PIN_DESC_LEN);
>>>+		dpll_pin_register(bp->dpll, pin, &dpll_pin_ops, bp);
>>
>>Same here, no point of having 2 step init.
>
>The alloc of a pin is not required if the pin already exist and would be just
>registered with another dpll.

Please don't. Have a pin created on a single DPLL. Why you make things
compitated here? I don't follow.


>Once we decide to entirely drop shared pins idea this could be probably done,
>although other kernel code usually use this twostep approach?

No, it does not. It's is used whatever fits on the individual usecase.


>
>>
>>
>>>+	}
>>>+
>>> 	return 0;
>>
>>
>>Btw, did you consider having dpll instance here as and auxdev? It would
>>be suitable I believe. It is quite simple to do it. See following patch
>>as an example:
>
>I haven't think about it, definetly gonna take a look to see if there any
>benefits in ice.

Please do. The proper separation and bus/device modelling is at least
one of the benefits. The other one is that all dpll drivers would
happily live in drivers/dpll/ side by side.



>
>Thanks,
>Arkadiusz
>
>>
>>commit bd02fd76d1909637c95e8ef13e7fd1e748af910d
>>Author: Jiri Pirko <jiri@nvidia.com>
>>Date:   Mon Jul 25 10:29:17 2022 +0200
>>
>>    mlxsw: core_linecards: Introduce per line card auxiliary device
>>
>>
>>
>>
>>>
>>> out:
>>>@@ -4247,6 +4294,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>>> 	struct devlink *devlink = priv_to_devlink(bp);
>>>
>>>+	dpll_device_unregister(bp->dpll);
>>>+	dpll_device_free(bp->dpll);
>>> 	devlink_unregister(devlink);
>>> 	ptp_ocp_detach(bp);
>>> 	pci_disable_device(pdev);
>>>--
>>>2.27.0
>>>

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

* RE: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-02 12:48       ` Jiri Pirko
@ 2022-12-02 14:39         ` Kubalewski, Arkadiusz
  2022-12-02 16:20           ` Jiri Pirko
  2022-12-07  2:33           ` Jakub Kicinski
  0 siblings, 2 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-02 14:39 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, December 2, 2022 1:49 PM
>
>Fri, Dec 02, 2022 at 12:27:32PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, November 30, 2022 1:41 PM
>>>
>>>Tue, Nov 29, 2022 at 10:37:24PM CET, vfedorenko@novek.ru wrote:
>>>>From: Vadim Fedorenko <vadfed@fb.com>
>
>[...]
>
>
>>>>+static int ptp_ocp_dpll_get_attr(struct dpll_device *dpll, struct
>>>dpll_attr *attr)
>>>>+{
>>>>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>>>>+	int sync;
>>>>+
>>>>+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>>>>+	dpll_attr_lock_status_set(attr, sync ? DPLL_LOCK_STATUS_LOCKED :
>>>DPLL_LOCK_STATUS_UNLOCKED);
>>>
>>>get,set,confuse. This attr thing sucks, sorry :/
>>
>>Once again, I feel obligated to add some explanations :)
>>
>>getter is ops called by dpll subsystem, it requires data, so here value
>>shall be set for the caller, right?
>>Also have explained the reason why this attr struct and functions are
>>done this way in the response to cover letter concerns.
>
>Okay, I will react there.

Thanks!

>
>
>>
>>>
>>>
>>>>+
>>>>+	return 0;
>>>>+}
>>>>+
>>>>+static int ptp_ocp_dpll_pin_get_attr(struct dpll_device *dpll,
>>>>+struct
>>>dpll_pin *pin,
>>>>+				     struct dpll_pin_attr *attr) {
>>>>+	dpll_pin_attr_type_set(attr, DPLL_PIN_TYPE_EXT);
>>>
>>>This is exactly what I was talking about in the cover letter. This is
>>>const, should be put into static struct and passed to
>>>dpll_device_alloc().
>>
>>Actually this type or some other parameters might change in the
>>run-time,
>
>No. This should not change.
>If the pin is SyncE port, it's that for all lifetime of pin. It cannot turn
>to be a EXT/SMA connector all of the sudden. This should be definitelly
>fixed, it's a device topology.
>
>Can you explain the exact scenario when the change of personality of pin
>can happen? Perhaps I'm missing something.
>

Our device is not capable of doing this type of switch, but why to assume
that some other HW would not? As I understand generic dpll subsystem must not
be tied to any HW, and you proposal makes it exactly tied to our approaches.
As Vadim requested to have possibility to change pin between source/output
"states" this seems also possible that some HW might have multiple types
possible.
I don't get why "all of the sudden", DPLLA_PIN_TYPE_SUPPORTED can have multiple
values, which means that the user can pick one of those with set command.
Then if HW supports it could redirect signals/setup things accordingly.

>
>
>>depends on the device, it is up to the driver how it will handle any
>>getter, if driver knows it won't change it could also have some static
>>member and copy the data with: dpll_pin_attr_copy(...);
>>
>>>
>>>
>>>>+	return 0;
>>>>+}
>>>>+
>>>>+static struct dpll_device_ops dpll_ops = {
>>>>+	.get	= ptp_ocp_dpll_get_attr,
>>>>+};
>>>>+
>>>>+static struct dpll_pin_ops dpll_pin_ops = {
>>>>+	.get	= ptp_ocp_dpll_pin_get_attr,
>>>>+};
>>>>+
>>>> static int
>>>> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>>>> {
>>>>+	const u8 dpll_cookie[DPLL_COOKIE_LEN] = { "OCP" };
>>>>+	char pin_desc[PIN_DESC_LEN];
>>>> 	struct devlink *devlink;
>>>>+	struct dpll_pin *pin;
>>>> 	struct ptp_ocp *bp;
>>>>-	int err;
>>>>+	int err, i;
>>>>
>>>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev-
>>>>dev);
>>>> 	if (!devlink) {
>>>>@@ -4230,6 +4263,20 @@ ptp_ocp_probe(struct pci_dev *pdev, const
>>>>struct
>>>pci_device_id *id)
>>>>
>>>> 	ptp_ocp_info(bp);
>>>> 	devlink_register(devlink);
>>>>+
>>>>+	bp->dpll = dpll_device_alloc(&dpll_ops, DPLL_TYPE_PPS, dpll_cookie,
>>>pdev->bus->number, bp, &pdev->dev);
>>>>+	if (!bp->dpll) {
>>>>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>>>>+		goto out;
>>>>+	}
>>>>+	dpll_device_register(bp->dpll);
>>>
>>>You still have the 2 step init process. I believe it would be better
>>>to just have dpll_device_create/destroy() to do it in one shot.
>>
>>For me either is ok, but due to pins alloc/register as explained below
>>I would leave it as it is.
>
>Please don't, it has no value. Just adds unnecesary code. Have it nice and
>simple.
>

Actually this comment relates to the other commit, could we keep comments
in the threads they belong to please, this would be much easier to track.
But yeah sure, if there is no strong opinion on that we could change it.

>
>>
>>>
>>>
>>>>+
>>>>+	for (i = 0; i < 4; i++) {
>>>>+		snprintf(pin_desc, PIN_DESC_LEN, "sma%d", i + 1);
>>>>+		pin = dpll_pin_alloc(pin_desc, PIN_DESC_LEN);
>>>>+		dpll_pin_register(bp->dpll, pin, &dpll_pin_ops, bp);
>>>
>>>Same here, no point of having 2 step init.
>>
>>The alloc of a pin is not required if the pin already exist and would
>>be just registered with another dpll.
>
>Please don't. Have a pin created on a single DPLL. Why you make things
>compitated here? I don't follow.

Tried to explain on the cover-letter thread, let's discuss there please.

>
>
>>Once we decide to entirely drop shared pins idea this could be probably
>>done, although other kernel code usually use this twostep approach?
>
>No, it does not. It's is used whatever fits on the individual usecase.

Similar to above, no strong opinion here from me, for shared pin it is
certainly useful.

>
>
>>
>>>
>>>
>>>>+	}
>>>>+
>>>> 	return 0;
>>>
>>>
>>>Btw, did you consider having dpll instance here as and auxdev? It
>>>would be suitable I believe. It is quite simple to do it. See
>>>following patch as an example:
>>
>>I haven't think about it, definetly gonna take a look to see if there
>>any benefits in ice.
>
>Please do. The proper separation and bus/device modelling is at least one
>of the benefits. The other one is that all dpll drivers would happily live
>in drivers/dpll/ side by side.
>

Well, makes sense, but still need to take a closer look on that.
I could do that on ice-driver part, don't feel strong enough yet to introduce
Changes here in ptp_ocp.

Thank you,
Arkadiusz

>
>
>>
>>Thanks,
>>Arkadiusz
>>
>>>
>>>commit bd02fd76d1909637c95e8ef13e7fd1e748af910d
>>>Author: Jiri Pirko <jiri@nvidia.com>
>>>Date:   Mon Jul 25 10:29:17 2022 +0200
>>>
>>>    mlxsw: core_linecards: Introduce per line card auxiliary device
>>>
>>>
>>>
>>>
>>>>
>>>> out:
>>>>@@ -4247,6 +4294,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>>>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>>>> 	struct devlink *devlink = priv_to_devlink(bp);
>>>>
>>>>+	dpll_device_unregister(bp->dpll);
>>>>+	dpll_device_free(bp->dpll);
>>>> 	devlink_unregister(devlink);
>>>> 	ptp_ocp_detach(bp);
>>>> 	pci_disable_device(pdev);
>>>>--
>>>>2.27.0
>>>>

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

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-02 12:39       ` Jiri Pirko
@ 2022-12-02 14:54         ` Kubalewski, Arkadiusz
  2022-12-02 16:15           ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-02 14:54 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk, Olech,
	Milena, Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, December 2, 2022 1:40 PM
>
>Fri, Dec 02, 2022 at 12:27:35PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, November 30, 2022 5:37 PM Tue, Nov 29, 2022 at
>>>10:37:22PM CET, vfedorenko@novek.ru wrote:
>>>>From: Vadim Fedorenko <vadfed@fb.com>
>
>[...]
>
>
>>>>+static int
>>>>+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct
>>>dpll_pin_attr *attr)
>>>>+{
>>>>+	unsigned int netifindex; // TODO: Should be u32?
>>>>+
>>>>+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
>>>>+		return 0;
>>>>+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>>>
>>>I was thinking about this. It is problematic. DPLL has no notion of
>>>network namespaces. So if the driver passes ifindex, dpll/user has no
>>>clue in which network namespace it is (ifindexes ovelay in multiple
>>>namespaces).
>>>
>>>There is no easy/nice solution. For now, I would go without this and
>>>only have linkage the opposite direction, from netdev to dpll.
>>
>>Well, makes sense to me.
>>Although as I have checked `ip a` showed the same ifindex either if
>>port was in the namespace or not.
>
>That is not the problem. The problem is, that you can have following two
>netdevs with the same ifindex each in different netns.
>1) netdev x: ifindex 8, netns ns1
>2) netdev y: ifindex 8, netns ns2
>

OK, I now see your point what is the confusion.
Thanks for explanation.
But I am still not sure how to make it this way in Linux, if interface added to
netns uses original netdev ifindex, and driver after reload receives new
(previously unused ifindex) what would be the steps/commands to make it as you
described? 

>>Isn't it better to let the user know ifindex, even if he has to iterate
>>all the namespaces he has created?
>
>Definitelly not. As I showed above, one ifindex may refer to multiple
>netdevice instances.
>
>
>[...]
>
>
>>>>+	DPLLA_NETIFINDEX,
>>>
>>>Duplicate, you have it under pin.
>>
>>The pin can have netifindex as pin signal source may originate there by
>>Clock recovery mechanics.
>>The dpll can have ifindex as it "owns" the dpll.
>
>DPLL is not owned by any netdevice. That does not make any sense.
>Netdevice may be "child" of the same PCI device as the dpll instance.
>But that's it.

Sure, I won't insist on having it there, as I said, thought Maciej have seen
a benefit with such traceability, unfortunately I cannot recall what was it.


Thanks,
Arkadiusz
 
>
>
>>Shall user know about it? probably nothing usefull for him, although
>>didn't Maciej Machnikowski asked to have such traceability?

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-02 11:27   ` Kubalewski, Arkadiusz
@ 2022-12-02 16:12     ` Jiri Pirko
  2022-12-07  2:47       ` Jakub Kicinski
  2022-12-08  0:27       ` Kubalewski, Arkadiusz
  0 siblings, 2 replies; 87+ messages in thread
From: Jiri Pirko @ 2022-12-02 16:12 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk

Fri, Dec 02, 2022 at 12:27:24PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, November 30, 2022 1:32 PM
>>
>>Tue, Nov 29, 2022 at 10:37:20PM CET, vfedorenko@novek.ru wrote:
>>>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.
>>
>>Overall, I see a lot of issues on multiple levels. I will go over them in
>>follow-up emails. So far, after couple of hours looking trought this, I
>>have following general feelings/notes:
>
>Hi Jiri,
>
>As we have been participating in last version, feel obligated to answer to
>the concerns.

Cool.


> 
>>
>>1) Netlink interface looks much saner than in previous versions. I will
>>   send couple of notes, mainly around events and object mixtures and
>>   couple of bugs/redundancies. But overall, looks fineish.
>>
>>2) I don't like that concept of a shared pin, at all. It makes things
>>   unnecessary complicated. Just have a pin created for dpll instance
>>   and that's it. If another instance has the same pin, it should create
>>   it as well. Keeps things separate and easy to model. Let the
>>   hw/fw/driver figure out the implementation oddities.
>>   Why exactly you keep pushing the shared pin idea? Perhaps I'm missing
>>   something crucial.
>
>
>If the user request change on pin#0 of dpll#0, the dpll#0 knows about the
>change, it reacts accordingly, and notifies the user the something has changed.
>Which is rather simple.
>
>Now, if the dpll#1 is using the same pin (pin#0 of dpll#0), the complicated
>part starts. First we have to assume:
>- it was initialized with the same description (as it should, to prevent
>confusing the user)
>- it was initialized with the same order (this is at least nice to have from
>user POV, as pin indices are auto generated), and also in case of multiple pins
>being shared it would be best for the user to have exactly the same number of
>pins initialized, so they have same indices and initialization order doesn't
>introduce additional confusion.
>
>Thus, one reason of shared pins was to prevent having this assumptions ever.
>If the pin is shared, all dplls sharing a pin would have the same description
>and pin index for a shared pin out of the box.
>
>Pin attribute changes
>The change on dpll#0 pin#0 impacts also dpll#1 pin#0. Notification about the
>change shall be also requested from the driver that handles dpll#1. In such
>case the driver has to have some dpll monitoring/notifying mechanics, which at
>first doesn't look very hard to do, but most likely only if both dplls are
>initialized and managed by a single instance of a driver/firmware.
>
>If board has 2 dplls but each one is managed by its own firmware/driver
>instance. User changes frequency of pin#0 signal, the driver of dpll#0 must
>also notify driver of dpll#1 that pin#0 frequency has changed, dpll#1 reacts on
>the change, notifies the user.

Right.


>But this is only doable with assumption, that the board is internally capable
>of such internal board level communication, which in case of separated
>firmwares handling multiple dplls might not be the case, or it would require
>to have some other sw component feel that gap.

Yep, you have the knowledge of sharing inside the driver, so you should
do it there. For multiple instances, use in-driver notifier for example.


>
>For complex boards with multiple dplls/sync channels, multiple ports,
>multiple firmware instances, it seems to be complicated to share a pin if
>each driver would have own copy and should notify all the other about changes.
>
>To summarize, that is certainly true, shared pins idea complicates stuff
>inside of dpll subsystem.
>But at the same time it removes complexity from all the drivers which would use

There are currently 3 drivers for dpll I know of. This in ptp_ocp and
mlx5 there is no concept of sharing pins. You you are talking about a
single driver.

What I'm trying to say is, looking at the code, the pin sharing,
references and locking makes things uncomfortably complex. You are so
far the only driver to need this, do it internally. If in the future
other driver appears, this code would be eventually pushed into dpll
core. No impact on UAPI from what I see. Please keep things as simple as
possible.


>it and is easier for the userspace due to common identification of pins.

By identification, you mean "description" right? I see no problem of 2
instances have the same pin "description"/label.


>This solution scales up without any additional complexity in the driver,
>and without any need for internal per-board communication channels.
>
>Not sure if this is good or bad, but with current version, both approaches are
>possible, so it pretty much depending on the driver to initialize dplls with
>separated pin objects as you have suggested (and take its complexity into
>driver) or just share them.
>
>>
>>3) I don't like the concept of muxed pins and hierarchies of pins. Why
>>   does user care? If pin is muxed, the rest of the pins related to this
>>   one should be in state disabled/disconnected. The user only cares
>>   about to see which pins are related to each other. It can be easily
>>   exposed by "muxid" like this:
>>   pin 1
>>   pin 2
>>   pin 3 muxid 100
>>   pin 4 muxid 100
>>   pin 5 muxid 101
>>   pin 6 muxid 101
>>   In this example pins 3,4 and 5,6 are muxed, therefore the user knows
>>   if he connects one, the other one gets disconnected (or will have to
>>   disconnect the first one explicitly first).
>>
>
>Currently DPLLA_PIN_PARENT_IDX is doing the same thing as you described, it
>groups MUXed pins, the parent pin index here was most straightforward to me,

There is a big difference if we model flat list of pins with a set of
attributes for each, comparing to a tree of pins, some acting as leaf,
node and root. Do we really need such complexicity? What value does it
bring to the user to expose this?


>as in case of DPLL_MODE_AUTOMATIC, where dpll auto-selects highest priority
>available signal. The priority can be only assigned to the pins directly
>connected to the dpll. The rest of pins (which would have present
>attribute DPLLA_PIN_PARENT_IDX) are the ones that require manual selection
>even if DPLL_MODE_AUTOMATIC is enabled.
>
>Enabling a particular pin and sub-pin in DPLL_MODE_AUTOMATIC requires from user
>to select proper priority on on a dpll-level MUX-pin and manually select one of
>the sub-pins.  
>On the other hand for DPLL_MODE_FORCED, this might be also beneficial, as the
>user could select a directly connected pin and muxed pin with two separated
>commands, which could be handled in separated driver instances (if HW design
>requires such approach) or either it can be handled just by one select call
>for the pin connected directly and handled entirely in the one driver instance.

Talk netlink please. What are the actual commands with cmds used to
select the source and select a mux pin? You are talking about 2 types of
selections:
1) Source select
   - this is as you described either auto/forces (manual) mode,
   according to some prio, dpll select the best source
2) Pin select in a mux
   - here the pin could be source or output

But again, from the user perspective, why does he have to distinguish
these? Extending my example:

   pin 1 source
   pin 2 output
   pin 3 muxid 100 source
   pin 4 muxid 100 source
   pin 5 muxid 101 source
   pin 6 muxid 101 source
   pin 7 output

User now can set individial prios for sources:

dpll x pin 1 set prio 10
etc
The result would be:

   pin 1 source prio 10
   pin 2 output
   pin 3 muxid 100 source prio 8
   pin 4 muxid 100 source prio 20
   pin 5 muxid 101 source prio 50
   pin 6 muxid 101 source prio 60
   pin 7 output

Now when auto is enabled, the pin 3 is selected. Why would user need to
manually select between 3 and 4? This is should be abstracted out by the
driver.

Only issues I see when pins are output. User would have to somehow
select one of the pins in the mux (perhaps another dpll cmd). But the
mux pin instance does not help with anything there...



>
>>4) I don't like the "attr" indirection. It makes things very tangled. It
>>   comes from the concepts of classes and objects and takes it to
>>   extreme. Not really something we are commonly used to in kernel.
>>   Also, it brings no value from what I can see, only makes things very
>>   hard to read and follow.
>>
>
>Yet again, true, I haven't find anything similar in the kernel, it was more
>like a try to find out a way to have a single structure with all the stuff that
>is passed between netlink/core/driver parts. Came up with this, and to be
>honest it suits pretty well, those are well defined containers. They store
>attributes that either user or driver have set, with ability to obtain a valid
>value only if it was set. Thus whoever reads a struct, knows which of those
>attributes were actually set.

Sorry for being blunt here, but when I saw the code I remembered my days
as a student where they forced us to do similar things Java :)
There you tend to make things contained, everything is a class, getters,
setters and whatnot. In kernel, this is overkill.

I'm not saying it's functionally wrong. What I say is that it is
completely unnecessary. I see no advantage, by having this indirection.
I see only disadvantages. It makes code harder to read and follow.
What I suggest, again, is to make things nice and simple. Set of ops
that the driver implements for dpll commands or parts of commands,
as we are used to in the rest of the kernel.


>As you said, seems a bit revolutionary, but IMHO it simplifies stuff, and
>basically it is value and validity bit, which I believe is rather common in the
>kernel, this differs only with the fact it is encapsulated. No direct access to
>the fields of structure is available for the users.

I don't see any reason for any validity bits whan you just do it using
driver-implemented ops.


>Most probably there are some things that could be improved with it, but in
>general it is very easy to use and understand how it works.
>What could be improved:
>- naming scheme as function names are a bit long right now, although mostly
>still fits the line-char limits, thus not that problematic
>- bit mask values are capable of storing 32 bits and bit(0) is always used as
>unspec, which ends up with 31 values available for the enums so if by any
>chance one of the attribute enums would go over 32 it could be an issue.
> 
>It is especially useful for multiple values passed with the same netlink
>attribute id. I.e. please take a look at dpll_msg_add_pin_types_supported(..)
>function.
>
>>   Please keep things direct and simple:
>>   * If some option could be changed for a pin or dpll, just have an
>>     op that is directly called from netlink handler to change it.
>>     There should be clear set of ops for configuration of pin and
>>     dpll object. This "attr" indirection make this totally invisible.
>
>In last review you have asked to have rather only set and get ops defined
>with a single attribute struct. This is exactly that, altough encapsulated.

For objects, yes. Pass a struct you directly read/write if the amount of
function args would be bigger then say 4. The whole encapsulation is not
good for anything.


>
>>   * If some attribute is const during dpll or pin lifetime, have it
>>     passed during dpll or pin creation.
>>
>>
>
>Only driver knows which attributes are const and which are not, this shall

Nonono. This is semantics defined by the subsystem. The pin
label/description for example. That is const, cannot be set by the user.
The type of the pin (synce/gnss/ext) is const, cannot be set by the user.
This you have to clearly specify when you define driver API.
This const attrs should be passed during pin creation/registration.

Talking about dpll instance itself, the clock_id, clock_quality, these
should be also const attrs.



>be also part of driver implementation.
>As I understand all the fields present in (dpll/dpll_pin)_attr, used in get/set
>ops, could be altered in run-time depending on HW design.
>
>Thanks,
>Arkadiusz
>
>>
>>>
>>>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 (1):
>>>  dpll: add dpll_attr/dpll_pin_attr helper classes
>>>
>>>Vadim Fedorenko (3):
>>>  dpll: Add DPLL framework base functions
>>>  dpll: documentation on DPLL subsystem interface
>>>  ptp_ocp: implement DPLL ops
>>>
>>> Documentation/networking/dpll.rst  | 271 ++++++++
>>> Documentation/networking/index.rst |   1 +
>>> MAINTAINERS                        |   8 +
>>> drivers/Kconfig                    |   2 +
>>> drivers/Makefile                   |   1 +
>>> drivers/dpll/Kconfig               |   7 +
>>> drivers/dpll/Makefile              |  11 +
>>> drivers/dpll/dpll_attr.c           | 278 +++++++++
>>> drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
>>> drivers/dpll/dpll_core.h           | 176 ++++++
>>> drivers/dpll/dpll_netlink.c        | 963 +++++++++++++++++++++++++++++
>>> drivers/dpll/dpll_netlink.h        |  24 +
>>> drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
>>> drivers/ptp/Kconfig                |   1 +
>>> drivers/ptp/ptp_ocp.c              | 123 ++--
>>> include/linux/dpll.h               | 261 ++++++++
>>> include/linux/dpll_attr.h          | 433 +++++++++++++
>>> include/uapi/linux/dpll.h          | 263 ++++++++
>>> 18 files changed, 4002 insertions(+), 37 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_attr.c 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/dpll/dpll_pin_attr.c create mode 100644 include/linux/dpll.h
>>> create mode 100644 include/linux/dpll_attr.h create mode 100644
>>> include/uapi/linux/dpll.h
>>>
>>>--
>>>2.27.0
>>>

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-02 14:54         ` Kubalewski, Arkadiusz
@ 2022-12-02 16:15           ` Jiri Pirko
       [not found]             ` <20221202212206.3619bd5f@kernel.org>
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-02 16:15 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk, Olech,
	Milena, Michalik, Michal

Fri, Dec 02, 2022 at 03:54:33PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, December 2, 2022 1:40 PM
>>
>>Fri, Dec 02, 2022 at 12:27:35PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Wednesday, November 30, 2022 5:37 PM Tue, Nov 29, 2022 at
>>>>10:37:22PM CET, vfedorenko@novek.ru wrote:
>>>>>From: Vadim Fedorenko <vadfed@fb.com>
>>
>>[...]
>>
>>
>>>>>+static int
>>>>>+dpll_msg_add_pin_netifindex(struct sk_buff *msg, const struct
>>>>dpll_pin_attr *attr)
>>>>>+{
>>>>>+	unsigned int netifindex; // TODO: Should be u32?
>>>>>+
>>>>>+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
>>>>>+		return 0;
>>>>>+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>>>>
>>>>I was thinking about this. It is problematic. DPLL has no notion of
>>>>network namespaces. So if the driver passes ifindex, dpll/user has no
>>>>clue in which network namespace it is (ifindexes ovelay in multiple
>>>>namespaces).
>>>>
>>>>There is no easy/nice solution. For now, I would go without this and
>>>>only have linkage the opposite direction, from netdev to dpll.
>>>
>>>Well, makes sense to me.
>>>Although as I have checked `ip a` showed the same ifindex either if
>>>port was in the namespace or not.
>>
>>That is not the problem. The problem is, that you can have following two
>>netdevs with the same ifindex each in different netns.
>>1) netdev x: ifindex 8, netns ns1
>>2) netdev y: ifindex 8, netns ns2
>>
>
>OK, I now see your point what is the confusion.
>Thanks for explanation.
>But I am still not sure how to make it this way in Linux, if interface added to
>netns uses original netdev ifindex, and driver after reload receives new
>(previously unused ifindex) what would be the steps/commands to make it as you
>described? 

As I said, I don't see a way to have the ifindex exposed throught dpll
at all. I believe we should do it only the other way around. Assign
dpll_pin pointer to struct net_device and expose this over new attr
IFLA_DPLL_PIN over RT netlink.


>
>>>Isn't it better to let the user know ifindex, even if he has to iterate
>>>all the namespaces he has created?
>>
>>Definitelly not. As I showed above, one ifindex may refer to multiple
>>netdevice instances.
>>
>>
>>[...]
>>
>>
>>>>>+	DPLLA_NETIFINDEX,
>>>>
>>>>Duplicate, you have it under pin.
>>>
>>>The pin can have netifindex as pin signal source may originate there by
>>>Clock recovery mechanics.
>>>The dpll can have ifindex as it "owns" the dpll.
>>
>>DPLL is not owned by any netdevice. That does not make any sense.
>>Netdevice may be "child" of the same PCI device as the dpll instance.
>>But that's it.
>
>Sure, I won't insist on having it there, as I said, thought Maciej have seen
>a benefit with such traceability, unfortunately I cannot recall what was it.
>
>
>Thanks,
>Arkadiusz
> 
>>
>>
>>>Shall user know about it? probably nothing usefull for him, although
>>>didn't Maciej Machnikowski asked to have such traceability?

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-02 14:39         ` Kubalewski, Arkadiusz
@ 2022-12-02 16:20           ` Jiri Pirko
  2022-12-08  0:35             ` Kubalewski, Arkadiusz
  2022-12-07  2:33           ` Jakub Kicinski
  1 sibling, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-02 16:20 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

Fri, Dec 02, 2022 at 03:39:17PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, December 2, 2022 1:49 PM
>>
>>Fri, Dec 02, 2022 at 12:27:32PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Wednesday, November 30, 2022 1:41 PM
>>>>
>>>>Tue, Nov 29, 2022 at 10:37:24PM CET, vfedorenko@novek.ru wrote:
>>>>>From: Vadim Fedorenko <vadfed@fb.com>
>>
>>[...]
>>
>>
>>>>>+static int ptp_ocp_dpll_get_attr(struct dpll_device *dpll, struct
>>>>dpll_attr *attr)
>>>>>+{
>>>>>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>>>>>+	int sync;
>>>>>+
>>>>>+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>>>>>+	dpll_attr_lock_status_set(attr, sync ? DPLL_LOCK_STATUS_LOCKED :
>>>>DPLL_LOCK_STATUS_UNLOCKED);
>>>>
>>>>get,set,confuse. This attr thing sucks, sorry :/
>>>
>>>Once again, I feel obligated to add some explanations :)
>>>
>>>getter is ops called by dpll subsystem, it requires data, so here value
>>>shall be set for the caller, right?
>>>Also have explained the reason why this attr struct and functions are
>>>done this way in the response to cover letter concerns.
>>
>>Okay, I will react there.
>
>Thanks!
>
>>
>>
>>>
>>>>
>>>>
>>>>>+
>>>>>+	return 0;
>>>>>+}
>>>>>+
>>>>>+static int ptp_ocp_dpll_pin_get_attr(struct dpll_device *dpll,
>>>>>+struct
>>>>dpll_pin *pin,
>>>>>+				     struct dpll_pin_attr *attr) {
>>>>>+	dpll_pin_attr_type_set(attr, DPLL_PIN_TYPE_EXT);
>>>>
>>>>This is exactly what I was talking about in the cover letter. This is
>>>>const, should be put into static struct and passed to
>>>>dpll_device_alloc().
>>>
>>>Actually this type or some other parameters might change in the
>>>run-time,
>>
>>No. This should not change.
>>If the pin is SyncE port, it's that for all lifetime of pin. It cannot turn
>>to be a EXT/SMA connector all of the sudden. This should be definitelly
>>fixed, it's a device topology.
>>
>>Can you explain the exact scenario when the change of personality of pin
>>can happen? Perhaps I'm missing something.
>>
>
>Our device is not capable of doing this type of switch, but why to assume
>that some other HW would not? As I understand generic dpll subsystem must not
>be tied to any HW, and you proposal makes it exactly tied to our approaches.
>As Vadim requested to have possibility to change pin between source/output
>"states" this seems also possible that some HW might have multiple types
>possible.

How? How do you physically change from EXT connector to SyncE port? That
does not make sense. Topology is given. Let's go back to Earth here.


>I don't get why "all of the sudden", DPLLA_PIN_TYPE_SUPPORTED can have multiple
>values, which means that the user can pick one of those with set command.
>Then if HW supports it could redirect signals/setup things accordingly.

We have to stritly distinguis between things that are given, wired-up,
static and things that could be configured.


>
>>
>>
>>>depends on the device, it is up to the driver how it will handle any
>>>getter, if driver knows it won't change it could also have some static
>>>member and copy the data with: dpll_pin_attr_copy(...);
>>>
>>>>
>>>>
>>>>>+	return 0;
>>>>>+}
>>>>>+
>>>>>+static struct dpll_device_ops dpll_ops = {
>>>>>+	.get	= ptp_ocp_dpll_get_attr,
>>>>>+};
>>>>>+
>>>>>+static struct dpll_pin_ops dpll_pin_ops = {
>>>>>+	.get	= ptp_ocp_dpll_pin_get_attr,
>>>>>+};
>>>>>+
>>>>> static int
>>>>> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>>>>> {
>>>>>+	const u8 dpll_cookie[DPLL_COOKIE_LEN] = { "OCP" };
>>>>>+	char pin_desc[PIN_DESC_LEN];
>>>>> 	struct devlink *devlink;
>>>>>+	struct dpll_pin *pin;
>>>>> 	struct ptp_ocp *bp;
>>>>>-	int err;
>>>>>+	int err, i;
>>>>>
>>>>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev-
>>>>>dev);
>>>>> 	if (!devlink) {
>>>>>@@ -4230,6 +4263,20 @@ ptp_ocp_probe(struct pci_dev *pdev, const
>>>>>struct
>>>>pci_device_id *id)
>>>>>
>>>>> 	ptp_ocp_info(bp);
>>>>> 	devlink_register(devlink);
>>>>>+
>>>>>+	bp->dpll = dpll_device_alloc(&dpll_ops, DPLL_TYPE_PPS, dpll_cookie,
>>>>pdev->bus->number, bp, &pdev->dev);
>>>>>+	if (!bp->dpll) {
>>>>>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>>>>>+		goto out;
>>>>>+	}
>>>>>+	dpll_device_register(bp->dpll);
>>>>
>>>>You still have the 2 step init process. I believe it would be better
>>>>to just have dpll_device_create/destroy() to do it in one shot.
>>>
>>>For me either is ok, but due to pins alloc/register as explained below
>>>I would leave it as it is.
>>
>>Please don't, it has no value. Just adds unnecesary code. Have it nice and
>>simple.
>>
>
>Actually this comment relates to the other commit, could we keep comments
>in the threads they belong to please, this would be much easier to track.
>But yeah sure, if there is no strong opinion on that we could change it.

Ok.


>
>>
>>>
>>>>
>>>>
>>>>>+
>>>>>+	for (i = 0; i < 4; i++) {
>>>>>+		snprintf(pin_desc, PIN_DESC_LEN, "sma%d", i + 1);
>>>>>+		pin = dpll_pin_alloc(pin_desc, PIN_DESC_LEN);
>>>>>+		dpll_pin_register(bp->dpll, pin, &dpll_pin_ops, bp);
>>>>
>>>>Same here, no point of having 2 step init.
>>>
>>>The alloc of a pin is not required if the pin already exist and would
>>>be just registered with another dpll.
>>
>>Please don't. Have a pin created on a single DPLL. Why you make things
>>compitated here? I don't follow.
>
>Tried to explain on the cover-letter thread, let's discuss there please.

Ok.


>
>>
>>
>>>Once we decide to entirely drop shared pins idea this could be probably
>>>done, although other kernel code usually use this twostep approach?
>>
>>No, it does not. It's is used whatever fits on the individual usecase.
>
>Similar to above, no strong opinion here from me, for shared pin it is
>certainly useful.
>
>>
>>
>>>
>>>>
>>>>
>>>>>+	}
>>>>>+
>>>>> 	return 0;
>>>>
>>>>
>>>>Btw, did you consider having dpll instance here as and auxdev? It
>>>>would be suitable I believe. It is quite simple to do it. See
>>>>following patch as an example:
>>>
>>>I haven't think about it, definetly gonna take a look to see if there
>>>any benefits in ice.
>>
>>Please do. The proper separation and bus/device modelling is at least one
>>of the benefits. The other one is that all dpll drivers would happily live
>>in drivers/dpll/ side by side.
>>
>
>Well, makes sense, but still need to take a closer look on that.
>I could do that on ice-driver part, don't feel strong enough yet to introduce

Sure Ice should be ready.


>Changes here in ptp_ocp.

I think that Vadim said he is going to look at that during the call. My
commit introducing this to mlxsw is a nice and simple example how this
could be done in ptp_ocp.


>
>Thank you,
>Arkadiusz
>
>>
>>
>>>
>>>Thanks,
>>>Arkadiusz
>>>
>>>>
>>>>commit bd02fd76d1909637c95e8ef13e7fd1e748af910d
>>>>Author: Jiri Pirko <jiri@nvidia.com>
>>>>Date:   Mon Jul 25 10:29:17 2022 +0200
>>>>
>>>>    mlxsw: core_linecards: Introduce per line card auxiliary device
>>>>
>>>>
>>>>
>>>>
>>>>>
>>>>> out:
>>>>>@@ -4247,6 +4294,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>>>>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>>>>> 	struct devlink *devlink = priv_to_devlink(bp);
>>>>>
>>>>>+	dpll_device_unregister(bp->dpll);
>>>>>+	dpll_device_free(bp->dpll);
>>>>> 	devlink_unregister(devlink);
>>>>> 	ptp_ocp_detach(bp);
>>>>> 	pci_disable_device(pdev);
>>>>>--
>>>>>2.27.0
>>>>>

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
       [not found]             ` <20221202212206.3619bd5f@kernel.org>
@ 2022-12-05 10:32               ` Jiri Pirko
  2022-12-06  0:19                 ` Jakub Kicinski
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-05 10:32 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

Sat, Dec 03, 2022 at 06:22:06AM CET, kuba@kernel.org wrote:
>On Fri, 2 Dec 2022 17:15:23 +0100 Jiri Pirko wrote:
>> >OK, I now see your point what is the confusion.
>> >Thanks for explanation.
>> >But I am still not sure how to make it this way in Linux, if interface added to
>> >netns uses original netdev ifindex, and driver after reload receives new
>> >(previously unused ifindex) what would be the steps/commands to make it as you
>> >described?
>>
>> As I said, I don't see a way to have the ifindex exposed throught dpll
>> at all.
>
>We can quite easily only report ifindexes in the same namespace
>as the socket, right?

Sure, hmm, thinkign about it more, this would be probably a good start.


>
>> I believe we should do it only the other way around. Assign
>> dpll_pin pointer to struct net_device and expose this over new attr
>> IFLA_DPLL_PIN over RT netlink.
>
>The ID table is global, what's the relationship between DPLLs
>and net namespaces? We tie DPLLs to a devlink instance which
>has a namespace? We pretend namespaces don't exist? :S

Well, if would be odd to put dpll itself into a namespace. It might not
have anything to do with networking, for example in case of ptp_ocp.
What would mean for a dpll to be in a net namespace?

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-05 10:32               ` Jiri Pirko
@ 2022-12-06  0:19                 ` Jakub Kicinski
  2022-12-06  8:50                   ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-06  0:19 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

On Mon, 5 Dec 2022 11:32:04 +0100 Jiri Pirko wrote:
> >> I believe we should do it only the other way around. Assign
> >> dpll_pin pointer to struct net_device and expose this over new attr
> >> IFLA_DPLL_PIN over RT netlink.  
> >
> >The ID table is global, what's the relationship between DPLLs
> >and net namespaces? We tie DPLLs to a devlink instance which
> >has a namespace? We pretend namespaces don't exist? :S  
> 
> Well, if would be odd to put dpll itself into a namespace. It might not
> have anything to do with networking, for example in case of ptp_ocp.
> What would mean for a dpll to be in a net namespace?

Yeah, that's a slightly tricky one. We'd probably need some form 
of second order association. Easiest if we link it to a devlink
instance, I reckon. The OCP clock card does not have netdevs so we
can't follow the namespace of netdevs (which would be the second
option).

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-06  0:19                 ` Jakub Kicinski
@ 2022-12-06  8:50                   ` Jiri Pirko
  2022-12-06 17:27                     ` Jakub Kicinski
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-06  8:50 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

Tue, Dec 06, 2022 at 01:19:33AM CET, kuba@kernel.org wrote:
>On Mon, 5 Dec 2022 11:32:04 +0100 Jiri Pirko wrote:
>> >> I believe we should do it only the other way around. Assign
>> >> dpll_pin pointer to struct net_device and expose this over new attr
>> >> IFLA_DPLL_PIN over RT netlink.  
>> >
>> >The ID table is global, what's the relationship between DPLLs
>> >and net namespaces? We tie DPLLs to a devlink instance which
>> >has a namespace? We pretend namespaces don't exist? :S  
>> 
>> Well, if would be odd to put dpll itself into a namespace. It might not
>> have anything to do with networking, for example in case of ptp_ocp.
>> What would mean for a dpll to be in a net namespace?
>
>Yeah, that's a slightly tricky one. We'd probably need some form 
>of second order association. Easiest if we link it to a devlink
>instance, I reckon. The OCP clock card does not have netdevs so we
>can't follow the namespace of netdevs (which would be the second
>option).

Why do we need this association at all?


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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-06  8:50                   ` Jiri Pirko
@ 2022-12-06 17:27                     ` Jakub Kicinski
  2022-12-07 13:10                       ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-06 17:27 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

On Tue, 6 Dec 2022 09:50:19 +0100 Jiri Pirko wrote:
>> Yeah, that's a slightly tricky one. We'd probably need some form 
>> of second order association. Easiest if we link it to a devlink
>> instance, I reckon. The OCP clock card does not have netdevs so we
>> can't follow the namespace of netdevs (which would be the second
>> option).  
> 
> Why do we need this association at all?

Someone someday may want netns delegation and if we don't have the
support from the start we may break backward compat introducing it.

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-02 14:39         ` Kubalewski, Arkadiusz
  2022-12-02 16:20           ` Jiri Pirko
@ 2022-12-07  2:33           ` Jakub Kicinski
  2022-12-07 13:19             ` Jiri Pirko
  1 sibling, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-07  2:33 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jiri Pirko, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, netdev,
	Vadim Fedorenko, linux-arm-kernel, linux-clk

On Fri, 2 Dec 2022 14:39:17 +0000 Kubalewski, Arkadiusz wrote:
> >>>Btw, did you consider having dpll instance here as and auxdev? It
> >>>would be suitable I believe. It is quite simple to do it. See
> >>>following patch as an example:  
> >>
> >>I haven't think about it, definetly gonna take a look to see if there
> >>any benefits in ice.  
> >
> >Please do. The proper separation and bus/device modelling is at least one
> >of the benefits. The other one is that all dpll drivers would happily live
> >in drivers/dpll/ side by side.
> 
> Well, makes sense, but still need to take a closer look on that.
> I could do that on ice-driver part, don't feel strong enough yet to introduce
> Changes here in ptp_ocp.

FWIW auxdev makes absolutely no sense to me for DPLL :/
So Jiri, please say why.


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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-02 16:12     ` Jiri Pirko
@ 2022-12-07  2:47       ` Jakub Kicinski
  2022-12-07 14:09         ` netdev.dump
  2022-12-07 14:51         ` Jiri Pirko
  2022-12-08  0:27       ` Kubalewski, Arkadiusz
  1 sibling, 2 replies; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-07  2:47 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk

On Fri, 2 Dec 2022 17:12:06 +0100 Jiri Pirko wrote:
> >But this is only doable with assumption, that the board is internally capable
> >of such internal board level communication, which in case of separated
> >firmwares handling multiple dplls might not be the case, or it would require
> >to have some other sw component feel that gap.  
> 
> Yep, you have the knowledge of sharing inside the driver, so you should
> do it there. For multiple instances, use in-driver notifier for example.

No, complexity in the drivers is not a good idea. The core should cover
the complexity and let the drivers be simple.

> >For complex boards with multiple dplls/sync channels, multiple ports,
> >multiple firmware instances, it seems to be complicated to share a pin if
> >each driver would have own copy and should notify all the other about changes.
> >
> >To summarize, that is certainly true, shared pins idea complicates stuff
> >inside of dpll subsystem.
> >But at the same time it removes complexity from all the drivers which would use  
> 
> There are currently 3 drivers for dpll I know of. This in ptp_ocp and
> mlx5 there is no concept of sharing pins. You you are talking about a
> single driver.
> 
> What I'm trying to say is, looking at the code, the pin sharing,
> references and locking makes things uncomfortably complex. You are so
> far the only driver to need this, do it internally. If in the future
> other driver appears, this code would be eventually pushed into dpll
> core. No impact on UAPI from what I see. Please keep things as simple as
> possible.

But the pin is shared for one driver. Who cares if it's not shared in
another. The user space must be able to reason about the constraints.

You are suggesting drivers to magically flip state in core objects
because of some hidden dependencies?!

> >it and is easier for the userspace due to common identification of pins.  
> 
> By identification, you mean "description" right? I see no problem of 2
> instances have the same pin "description"/label.
>
> >This solution scales up without any additional complexity in the driver,
> >and without any need for internal per-board communication channels.
> >
> >Not sure if this is good or bad, but with current version, both approaches are
> >possible, so it pretty much depending on the driver to initialize dplls with
> >separated pin objects as you have suggested (and take its complexity into
> >driver) or just share them.
> >  
> >>
> >>3) I don't like the concept of muxed pins and hierarchies of pins. Why
> >>   does user care? If pin is muxed, the rest of the pins related to this
> >>   one should be in state disabled/disconnected. The user only cares
> >>   about to see which pins are related to each other. It can be easily
> >>   exposed by "muxid" like this:
> >>   pin 1
> >>   pin 2
> >>   pin 3 muxid 100
> >>   pin 4 muxid 100
> >>   pin 5 muxid 101
> >>   pin 6 muxid 101
> >>   In this example pins 3,4 and 5,6 are muxed, therefore the user knows
> >>   if he connects one, the other one gets disconnected (or will have to
> >>   disconnect the first one explicitly first).
> >
> >Currently DPLLA_PIN_PARENT_IDX is doing the same thing as you described, it
> >groups MUXed pins, the parent pin index here was most straightforward to me,  
> 
> There is a big difference if we model flat list of pins with a set of
> attributes for each, comparing to a tree of pins, some acting as leaf,
> node and root. Do we really need such complexicity? What value does it
> bring to the user to expose this?

The fact that you can't auto select from devices behind muxes.
The HW topology is of material importance to user space.
How many times does Arkadiusz have to explain this :|

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-06 17:27                     ` Jakub Kicinski
@ 2022-12-07 13:10                       ` Jiri Pirko
  2022-12-07 16:59                         ` Jakub Kicinski
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-07 13:10 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

Tue, Dec 06, 2022 at 06:27:05PM CET, kuba@kernel.org wrote:
>On Tue, 6 Dec 2022 09:50:19 +0100 Jiri Pirko wrote:
>>> Yeah, that's a slightly tricky one. We'd probably need some form 
>>> of second order association. Easiest if we link it to a devlink
>>> instance, I reckon. The OCP clock card does not have netdevs so we
>>> can't follow the namespace of netdevs (which would be the second
>>> option).  
>> 
>> Why do we need this association at all?
>
>Someone someday may want netns delegation and if we don't have the
>support from the start we may break backward compat introducing it.

Hmm. Can you imagine a usecase?

Link to devlink instance btw might be a problem. In case of mlx5, one
dpll instance is going to be created for 2 (or more) PFs. 1 per ConnectX
ASIC as there is only 1 clock there. And PF devlinks can come and go,
does not make sense to link it to any of them.

Thinking about it a bit more, DPLL itself has no network notion. The
special case is SyncE pin, which is linked to netdevice. Just a small
part of dpll device. And the netdevice already has notion of netns.
Isn't that enough?

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-07  2:33           ` Jakub Kicinski
@ 2022-12-07 13:19             ` Jiri Pirko
       [not found]               ` <20221207090524.3f562eeb@kernel.org>
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-07 13:19 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk

Wed, Dec 07, 2022 at 03:33:13AM CET, kuba@kernel.org wrote:
>On Fri, 2 Dec 2022 14:39:17 +0000 Kubalewski, Arkadiusz wrote:
>> >>>Btw, did you consider having dpll instance here as and auxdev? It
>> >>>would be suitable I believe. It is quite simple to do it. See
>> >>>following patch as an example:  
>> >>
>> >>I haven't think about it, definetly gonna take a look to see if there
>> >>any benefits in ice.  
>> >
>> >Please do. The proper separation and bus/device modelling is at least one
>> >of the benefits. The other one is that all dpll drivers would happily live
>> >in drivers/dpll/ side by side.
>> 
>> Well, makes sense, but still need to take a closer look on that.
>> I could do that on ice-driver part, don't feel strong enough yet to introduce
>> Changes here in ptp_ocp.
>
>FWIW auxdev makes absolutely no sense to me for DPLL :/
>So Jiri, please say why.

Why not? It's a subdev of a device. In mlx5, we have separate auxdevs
for eth, rdma, vnet, representors. DPLL is also a separate entity which
could be instantiated independently (as it is not really dependent on
eth/rdma/etc)). Auxdev looks like an awesome fit. Why do you think it is
not?

Also, what's good about auxdev is that you can maintain them quite
independetly. So there is going to be driver/dpll/ directory which would
contain all dpll drivers.



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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-07  2:47       ` Jakub Kicinski
@ 2022-12-07 14:09         ` netdev.dump
  2022-12-07 23:21           ` Jakub Kicinski
  2022-12-07 14:51         ` Jiri Pirko
  1 sibling, 1 reply; 87+ messages in thread
From: netdev.dump @ 2022-12-07 14:09 UTC (permalink / raw)
  To: 'Jakub Kicinski', 'Jiri Pirko'
  Cc: 'Kubalewski, Arkadiusz', 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

> -----Original Message-----
> From: Jakub Kicinski <kuba@kernel.org>
> Sent: Wednesday, December 7, 2022 3:48 AM
> Subject: Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
> 
> On Fri, 2 Dec 2022 17:12:06 +0100 Jiri Pirko wrote:
> > >But this is only doable with assumption, that the board is internally
capable
> > >of such internal board level communication, which in case of separated
> > >firmwares handling multiple dplls might not be the case, or it would
require
> > >to have some other sw component feel that gap.
> >
> > Yep, you have the knowledge of sharing inside the driver, so you should
> > do it there. For multiple instances, use in-driver notifier for example.
> 
> No, complexity in the drivers is not a good idea. The core should cover
> the complexity and let the drivers be simple.

But how does Driver A know where to connect its pin to? It makes sense to
share 
pins between the DPLLs exposed by a single driver, but not really outside of
it.
And that can be done simply by putting the pin ptr from the DPLLA into the
pin
list of DPLLB.

If we want the kitchen-and-sink solution, we need to think about corner
cases.
Which pin should the API give to the userspace app - original, or
muxed/parent?
How would a teardown look like - if Driver A registered DPLLA with Pin1 and
Driver B added the muxed pin then how should Driver A properly
release its pins? Should it just send a message to driver B and trust that
it
will receive it in time before we tear everything apart?

There are many problems with that approach, and the submitted patch is not
explaining any of them. E.g. it contains the dpll_muxed_pin_register but no
free 
counterpart + no flows.

If we want to get shared pins, we need a good example of how this mechanism
can be used.

> 
> > >For complex boards with multiple dplls/sync channels, multiple ports,
> > >multiple firmware instances, it seems to be complicated to share a pin
if
> > >each driver would have own copy and should notify all the other about
> changes.
> > >
> > >To summarize, that is certainly true, shared pins idea complicates
stuff
> > >inside of dpll subsystem.
> > >But at the same time it removes complexity from all the drivers which
would
> use
> >
> > There are currently 3 drivers for dpll I know of. This in ptp_ocp and
> > mlx5 there is no concept of sharing pins. You you are talking about a
> > single driver.
> >
> > What I'm trying to say is, looking at the code, the pin sharing,
> > references and locking makes things uncomfortably complex. You are so
> > far the only driver to need this, do it internally. If in the future
> > other driver appears, this code would be eventually pushed into dpll
> > core. No impact on UAPI from what I see. Please keep things as simple as
> > possible.
> 
> But the pin is shared for one driver. Who cares if it's not shared in
> another. The user space must be able to reason about the constraints.
> 
> You are suggesting drivers to magically flip state in core objects
> because of some hidden dependencies?!
> 

If we want to go outside the device, we'd need some universal language
to describe external connections - such as the devicetree. I don't see how
we can reliably implement inter-driver dependency otherwise.

I think this would be better served in the userspace with a board-specific
config file. Especially since the pins can be externally connected anyway.


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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-07  2:47       ` Jakub Kicinski
  2022-12-07 14:09         ` netdev.dump
@ 2022-12-07 14:51         ` Jiri Pirko
       [not found]           ` <20221207091946.3115742f@kernel.org>
  1 sibling, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-07 14:51 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk

Wed, Dec 07, 2022 at 03:47:40AM CET, kuba@kernel.org wrote:
>On Fri, 2 Dec 2022 17:12:06 +0100 Jiri Pirko wrote:
>> >But this is only doable with assumption, that the board is internally capable
>> >of such internal board level communication, which in case of separated
>> >firmwares handling multiple dplls might not be the case, or it would require
>> >to have some other sw component feel that gap.  
>> 
>> Yep, you have the knowledge of sharing inside the driver, so you should
>> do it there. For multiple instances, use in-driver notifier for example.
>
>No, complexity in the drivers is not a good idea. The core should cover
>the complexity and let the drivers be simple.

Really, even in case only one driver actually consumes the complexicity?
I understand having a "libs" to do common functionality for drivers,
even in case there is one. But this case, I don't see any benefit.


>
>> >For complex boards with multiple dplls/sync channels, multiple ports,
>> >multiple firmware instances, it seems to be complicated to share a pin if
>> >each driver would have own copy and should notify all the other about changes.
>> >
>> >To summarize, that is certainly true, shared pins idea complicates stuff
>> >inside of dpll subsystem.
>> >But at the same time it removes complexity from all the drivers which would use  
>> 
>> There are currently 3 drivers for dpll I know of. This in ptp_ocp and
>> mlx5 there is no concept of sharing pins. You you are talking about a
>> single driver.
>> 
>> What I'm trying to say is, looking at the code, the pin sharing,
>> references and locking makes things uncomfortably complex. You are so
>> far the only driver to need this, do it internally. If in the future
>> other driver appears, this code would be eventually pushed into dpll
>> core. No impact on UAPI from what I see. Please keep things as simple as
>> possible.
>
>But the pin is shared for one driver. Who cares if it's not shared in
>another. The user space must be able to reason about the constraints.

Sorry, I don't follow :/ Could you please explain what do you mean by
this?

>
>You are suggesting drivers to magically flip state in core objects
>because of some hidden dependencies?!

It's not a state flip. It's more like a well propagated event of a state
change. The async changes may happen anyway, so the userspace needs
to handle them. Why is this different?


>
>> >it and is easier for the userspace due to common identification of pins.  
>> 
>> By identification, you mean "description" right? I see no problem of 2
>> instances have the same pin "description"/label.
>>
>> >This solution scales up without any additional complexity in the driver,
>> >and without any need for internal per-board communication channels.
>> >
>> >Not sure if this is good or bad, but with current version, both approaches are
>> >possible, so it pretty much depending on the driver to initialize dplls with
>> >separated pin objects as you have suggested (and take its complexity into
>> >driver) or just share them.
>> >  
>> >>
>> >>3) I don't like the concept of muxed pins and hierarchies of pins. Why
>> >>   does user care? If pin is muxed, the rest of the pins related to this
>> >>   one should be in state disabled/disconnected. The user only cares
>> >>   about to see which pins are related to each other. It can be easily
>> >>   exposed by "muxid" like this:
>> >>   pin 1
>> >>   pin 2
>> >>   pin 3 muxid 100
>> >>   pin 4 muxid 100
>> >>   pin 5 muxid 101
>> >>   pin 6 muxid 101
>> >>   In this example pins 3,4 and 5,6 are muxed, therefore the user knows
>> >>   if he connects one, the other one gets disconnected (or will have to
>> >>   disconnect the first one explicitly first).
>> >
>> >Currently DPLLA_PIN_PARENT_IDX is doing the same thing as you described, it
>> >groups MUXed pins, the parent pin index here was most straightforward to me,  
>> 
>> There is a big difference if we model flat list of pins with a set of
>> attributes for each, comparing to a tree of pins, some acting as leaf,
>> node and root. Do we really need such complexicity? What value does it
>> bring to the user to expose this?
>
>The fact that you can't auto select from devices behind muxes.

Why? What's wrong with the mechanism I described in another part of this
thread?

Extending my example from above

   pin 1 source
   pin 2 output
   pin 3 muxid 100 source
   pin 4 muxid 100 source
   pin 5 muxid 101 source
   pin 6 muxid 101 source
   pin 7 output

User now can set individial prios for sources:

dpll x pin 1 set prio 10
etc
The result would be:

   pin 1 source prio 10
   pin 2 output
   pin 3 muxid 100 source prio 8
   pin 4 muxid 100 source prio 20
   pin 5 muxid 101 source prio 50
   pin 6 muxid 101 source prio 60
   pin 7 output

Now when auto is enabled, the pin 3 is selected. Why would user need to
manually select between 3 and 4? This is should be abstracted out by the
driver.

Actually, this is neat as you have one cmd to do selection in manual
mode and you have uniform way of configuring/monitoring selection in
autosel. Would the muxed pin make this better?

For muxed pin being output, only one pin from mux would be set:

   pin 1 source
   pin 2 output
   pin 3 muxid 100 disconnected
   pin 4 muxid 100 disconnected
   pin 5 muxid 101 output
   pin 6 muxid 101 disconnected
   pin 7 output


>The HW topology is of material importance to user space.

Interesting. When I was working on linecards, you said that the user
does not care about the inner HW topology. And it makes sense. When
things could be abstracted out to make iface clean, I think they should.


>How many times does Arkadiusz have to explain this :|

Pardon my ignorance, I don't see why exactly we need mux hierarchy
(trees) exposed to user.


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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-07 13:10                       ` Jiri Pirko
@ 2022-12-07 16:59                         ` Jakub Kicinski
  2022-12-08  8:14                           ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-07 16:59 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

On Wed, 7 Dec 2022 14:10:42 +0100 Jiri Pirko wrote:
> >> Why do we need this association at all?  
> >
> >Someone someday may want netns delegation and if we don't have the
> >support from the start we may break backward compat introducing it.  
> 
> Hmm. Can you imagine a usecase?

Running DPLL control in a namespace / container.

I mean - I generally think netns is overused, but yes, it's what
containers use, so I think someone may want to develop their
timer controller SW in as a container?

> Link to devlink instance btw might be a problem. In case of mlx5, one
> dpll instance is going to be created for 2 (or more) PFs. 1 per ConnectX
> ASIC as there is only 1 clock there. And PF devlinks can come and go,
> does not make sense to link it to any of them.

If only we stuck to the "one devlink instance per ASIC", huh? :)

> Thinking about it a bit more, DPLL itself has no network notion. The
> special case is SyncE pin, which is linked to netdevice. Just a small
> part of dpll device. And the netdevice already has notion of netns.
> Isn't that enough?

So we can't use devlink or netdev. Hm. So what do we do?
Make DPLLs only visible in init_net? And require init_net admin?
And when someone comes asking we add an explicit "move to netns"
command to DPLL?

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-07 14:09         ` netdev.dump
@ 2022-12-07 23:21           ` Jakub Kicinski
  2022-12-08 11:28             ` Jiri Pirko
                               ` (2 more replies)
  0 siblings, 3 replies; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-07 23:21 UTC (permalink / raw)
  To: netdev.dump
  Cc: 'Jiri Pirko', 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

On Wed, 7 Dec 2022 15:09:03 +0100 netdev.dump@gmail.com wrote:
> > -----Original Message-----
> > From: Jakub Kicinski <kuba@kernel.org>
> > Sent: Wednesday, December 7, 2022 3:48 AM
> > Subject: Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
> > 
> > On Fri, 2 Dec 2022 17:12:06 +0100 Jiri Pirko wrote:  
>  [...]  
> capable
>  [...]  
> require
>  [...]  

Please fix line wrapping in your email client. 
And add a name to your account configuration :/

> > > Yep, you have the knowledge of sharing inside the driver, so you should
> > > do it there. For multiple instances, use in-driver notifier for example.  
> > 
> > No, complexity in the drivers is not a good idea. The core should cover
> > the complexity and let the drivers be simple.  
> 
> But how does Driver A know where to connect its pin to? It makes sense to
> share 

I think we discussed using serial numbers.

> pins between the DPLLs exposed by a single driver, but not really outside of
> it.
> And that can be done simply by putting the pin ptr from the DPLLA into the
> pin
> list of DPLLB.

Are you saying within the driver it's somehow easier? The driver state
is mostly per bus device, so I don't see how.

> If we want the kitchen-and-sink solution, we need to think about corner
> cases.
> Which pin should the API give to the userspace app - original, or
> muxed/parent?

IDK if I parse but I think both. If selected pin is not directly
attached the core should configure muxes.

> How would a teardown look like - if Driver A registered DPLLA with Pin1 and
> Driver B added the muxed pin then how should Driver A properly
> release its pins? Should it just send a message to driver B and trust that
> it
> will receive it in time before we tear everything apart?

Trivial.

> There are many problems with that approach, and the submitted patch is not
> explaining any of them. E.g. it contains the dpll_muxed_pin_register but no
> free 
> counterpart + no flows.

SMOC.

> If we want to get shared pins, we need a good example of how this mechanism
> can be used.

Agreed.

> > > There are currently 3 drivers for dpll I know of. This in ptp_ocp and
> > > mlx5 there is no concept of sharing pins. You you are talking about a
> > > single driver.
> > >
> > > What I'm trying to say is, looking at the code, the pin sharing,
> > > references and locking makes things uncomfortably complex. You are so
> > > far the only driver to need this, do it internally. If in the future
> > > other driver appears, this code would be eventually pushed into dpll
> > > core. No impact on UAPI from what I see. Please keep things as simple as
> > > possible.  
> > 
> > But the pin is shared for one driver. Who cares if it's not shared in
> > another. The user space must be able to reason about the constraints.
> > 
> > You are suggesting drivers to magically flip state in core objects
> > because of some hidden dependencies?!
> 
> If we want to go outside the device, we'd need some universal language
> to describe external connections - such as the devicetree. I don't see how
> we can reliably implement inter-driver dependency otherwise.

There's plenty examples in the tree. If we can't use serial number
directly we can compare the driver pointer + whatever you'd compare
in the driver internal solution.

> I think this would be better served in the userspace with a board-specific
> config file. Especially since the pins can be externally connected anyway.

Opinions vary, progress is not being made.

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-02 16:12     ` Jiri Pirko
  2022-12-07  2:47       ` Jakub Kicinski
@ 2022-12-08  0:27       ` Kubalewski, Arkadiusz
  2022-12-08 11:58         ` Jiri Pirko
  1 sibling, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-08  0:27 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, December 2, 2022 5:12 PM
>
>Fri, Dec 02, 2022 at 12:27:24PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, November 30, 2022 1:32 PM
>>>
>>>Tue, Nov 29, 2022 at 10:37:20PM CET, vfedorenko@novek.ru wrote:
>>>>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.
>>>
>>>Overall, I see a lot of issues on multiple levels. I will go over them in
>>>follow-up emails. So far, after couple of hours looking trought this, I
>>>have following general feelings/notes:
>>
>>Hi Jiri,
>>
>>As we have been participating in last version, feel obligated to answer to
>>the concerns.
>
>Cool.
>
>
>>
>>>
>>>1) Netlink interface looks much saner than in previous versions. I will
>>>   send couple of notes, mainly around events and object mixtures and
>>>   couple of bugs/redundancies. But overall, looks fineish.
>>>
>>>2) I don't like that concept of a shared pin, at all. It makes things
>>>   unnecessary complicated. Just have a pin created for dpll instance
>>>   and that's it. If another instance has the same pin, it should create
>>>   it as well. Keeps things separate and easy to model. Let the
>>>   hw/fw/driver figure out the implementation oddities.
>>>   Why exactly you keep pushing the shared pin idea? Perhaps I'm missing
>>>   something crucial.
>>
>>
>>If the user request change on pin#0 of dpll#0, the dpll#0 knows about the
>>change, it reacts accordingly, and notifies the user the something has
>changed.
>>Which is rather simple.
>>
>>Now, if the dpll#1 is using the same pin (pin#0 of dpll#0), the
>complicated
>>part starts. First we have to assume:
>>- it was initialized with the same description (as it should, to prevent
>>confusing the user)
>>- it was initialized with the same order (this is at least nice to have
>from
>>user POV, as pin indices are auto generated), and also in case of multiple
>pins
>>being shared it would be best for the user to have exactly the same number
>of
>>pins initialized, so they have same indices and initialization order
>doesn't
>>introduce additional confusion.
>>
>>Thus, one reason of shared pins was to prevent having this assumptions
>ever.
>>If the pin is shared, all dplls sharing a pin would have the same
>description
>>and pin index for a shared pin out of the box.
>>
>>Pin attribute changes
>>The change on dpll#0 pin#0 impacts also dpll#1 pin#0. Notification about
>the
>>change shall be also requested from the driver that handles dpll#1. In
>such
>>case the driver has to have some dpll monitoring/notifying mechanics,
>which at
>>first doesn't look very hard to do, but most likely only if both dplls are
>>initialized and managed by a single instance of a driver/firmware.
>>
>>If board has 2 dplls but each one is managed by its own firmware/driver
>>instance. User changes frequency of pin#0 signal, the driver of dpll#0
>must
>>also notify driver of dpll#1 that pin#0 frequency has changed, dpll#1
>reacts on
>>the change, notifies the user.
>
>Right.
>
>
>>But this is only doable with assumption, that the board is internally
>capable
>>of such internal board level communication, which in case of separated
>>firmwares handling multiple dplls might not be the case, or it would
>require
>>to have some other sw component feel that gap.
>
>Yep, you have the knowledge of sharing inside the driver, so you should
>do it there. For multiple instances, use in-driver notifier for example.
>

This can be done right now, if driver doesn't need built-in pin sharing or
doesn't want it, it doesn't have to use it.
It was designed this way for a reason, reason of reduced complexity of drivers,
why should we want to increase complexity of multiple objects instead of having
it in one place?

>
>>
>>For complex boards with multiple dplls/sync channels, multiple ports,
>>multiple firmware instances, it seems to be complicated to share a pin if
>>each driver would have own copy and should notify all the other about
>changes.
>>
>>To summarize, that is certainly true, shared pins idea complicates stuff
>>inside of dpll subsystem.
>>But at the same time it removes complexity from all the drivers which
>would use
>
>There are currently 3 drivers for dpll I know of. This in ptp_ocp and
>mlx5 there is no concept of sharing pins. You you are talking about a
>single driver.
>
>What I'm trying to say is, looking at the code, the pin sharing,
>references and locking makes things uncomfortably complex. You are so
>far the only driver to need this, do it internally. If in the future
>other driver appears, this code would be eventually pushed into dpll
>core. No impact on UAPI from what I see. Please keep things as simple as
>possible.
>

The part of interface for drivers is right now 17 functions in total, thus I
wouldn't call it complicated. References between dpll instances and pins, are
other part of "increased" complexity, but I wouldn't either call it
over-complicated, besides it is part of dpll-core. Any interface shouldn't make
the user to look into the code. For users only documentation shall be important.
If kernel module developer is able to read header and understand what he needs
to do for his hardware, whether he want to use shared pins or not, and what
impact would it have.

Thus real question is, if it is easy enough to use both parts.
I would say the kernel module part is easy to understand even by looking at
the function names and their arguments. Proper documentation would clear
anything that is still not clear.

Netlink part is also moving in a right direction.

>
>>it and is easier for the userspace due to common identification of pins.
>
>By identification, you mean "description" right? I see no problem of 2
>instances have the same pin "description"/label.
>

"description"/label and index as they are the same pin. Index is auto-generated
and depends on initialization order, this makes unnecessary dependency.

>
>>This solution scales up without any additional complexity in the driver,
>>and without any need for internal per-board communication channels.
>>
>>Not sure if this is good or bad, but with current version, both approaches
>are
>>possible, so it pretty much depending on the driver to initialize dplls
>with
>>separated pin objects as you have suggested (and take its complexity into
>>driver) or just share them.
>>
>>>
>>>3) I don't like the concept of muxed pins and hierarchies of pins. Why
>>>   does user care? If pin is muxed, the rest of the pins related to this
>>>   one should be in state disabled/disconnected. The user only cares
>>>   about to see which pins are related to each other. It can be easily
>>>   exposed by "muxid" like this:
>>>   pin 1
>>>   pin 2
>>>   pin 3 muxid 100
>>>   pin 4 muxid 100
>>>   pin 5 muxid 101
>>>   pin 6 muxid 101
>>>   In this example pins 3,4 and 5,6 are muxed, therefore the user knows
>>>   if he connects one, the other one gets disconnected (or will have to
>>>   disconnect the first one explicitly first).
>>>
>>
>>Currently DPLLA_PIN_PARENT_IDX is doing the same thing as you described,
>it
>>groups MUXed pins, the parent pin index here was most straightforward to
>me,
>
>There is a big difference if we model flat list of pins with a set of
>attributes for each, comparing to a tree of pins, some acting as leaf,
>node and root. Do we really need such complexicity? What value does it
>bring to the user to expose this?
>

If the one doesn't need to use muxed pins, everything is simple, as you have
described. In fact in the example you have given, only benefit I can see from
muxid is that user knows which pins would get disconnected when one of the mux
group pins is selected. But do user really needs that knowledge? If one pin
was selected why should he look on the other pins?

Anyway I see your point, but this is not only about identifying a muxed pins,
it is more about identifying connections between them because of hardware
capabilities they can bring, and utilization of such capabilities.
Please take a look on a comment below.

>
>>as in case of DPLL_MODE_AUTOMATIC, where dpll auto-selects highest
>priority
>>available signal. The priority can be only assigned to the pins directly
>>connected to the dpll. The rest of pins (which would have present
>>attribute DPLLA_PIN_PARENT_IDX) are the ones that require manual selection
>>even if DPLL_MODE_AUTOMATIC is enabled.
>>
>>Enabling a particular pin and sub-pin in DPLL_MODE_AUTOMATIC requires from
>user
>>to select proper priority on on a dpll-level MUX-pin and manually select
>one of
>>the sub-pins.
>>On the other hand for DPLL_MODE_FORCED, this might be also beneficial, as
>the
>>user could select a directly connected pin and muxed pin with two
>separated
>>commands, which could be handled in separated driver instances (if HW
>design
>>requires such approach) or either it can be handled just by one select
>call
>>for the pin connected directly and handled entirely in the one driver
>instance.
>
>Talk netlink please. What are the actual commands with cmds used to
>select the source and select a mux pin? You are talking about 2 types of
>selections:
>1) Source select
>   - this is as you described either auto/forces (manual) mode,
>   according to some prio, dpll select the best source
>2) Pin select in a mux
>   - here the pin could be source or output
>
>But again, from the user perspective, why does he have to distinguish
>these? Extending my example:
>
>   pin 1 source
>   pin 2 output
>   pin 3 muxid 100 source
>   pin 4 muxid 100 source
>   pin 5 muxid 101 source
>   pin 6 muxid 101 source
>   pin 7 output
>
>User now can set individial prios for sources:
>
>dpll x pin 1 set prio 10
>etc
>The result would be:
>
>   pin 1 source prio 10
>   pin 2 output
>   pin 3 muxid 100 source prio 8
>   pin 4 muxid 100 source prio 20
>   pin 5 muxid 101 source prio 50
>   pin 6 muxid 101 source prio 60
>   pin 7 output
>
>Now when auto is enabled, the pin 3 is selected. Why would user need to
>manually select between 3 and 4? This is should be abstracted out by the
>driver.

Yes, this could be abstracted in the driver, but we are talking here about
different things. We need automatic hardware-supported selection of a pin,
not automatic software-supported selection, where driver (part of software
stack) is doing autoselection.

If dpll is capable of hw-autoselection, the priorities are configured in the
hardware, not in the driver. This interface is a proxy between user and dpll.
The one could use it as you have described by implementing auto-selection
in SW, but those are two different modes, actually we could introduce modes
like: DPLL_MODE_HW_AUTOMATIC and DPLL_MODE_SW_AUTOMATIC, to clear things up.
Although, IMHO, DPLL_MODE_SW_AUTOMATIC wouldn't really differ from current
behavior of DPLL_MODE_FORCED, where the userspace is explicitly selecting a
source, and the only difference would be that selection comes from driver
instead of userspace tool. Thus in the end it would just increase complexity
of a driver (instead of bringing any benefits).

>
>Only issues I see when pins are output. User would have to somehow
>select one of the pins in the mux (perhaps another dpll cmd). But the
>mux pin instance does not help with anything there...
>
>
>
>>
>>>4) I don't like the "attr" indirection. It makes things very tangled. It
>>>   comes from the concepts of classes and objects and takes it to
>>>   extreme. Not really something we are commonly used to in kernel.
>>>   Also, it brings no value from what I can see, only makes things very
>>>   hard to read and follow.
>>>
>>
>>Yet again, true, I haven't find anything similar in the kernel, it was
>more
>>like a try to find out a way to have a single structure with all the stuff
>that
>>is passed between netlink/core/driver parts. Came up with this, and to be
>>honest it suits pretty well, those are well defined containers. They store
>>attributes that either user or driver have set, with ability to obtain a
>valid
>>value only if it was set. Thus whoever reads a struct, knows which of
>those
>>attributes were actually set.
>
>Sorry for being blunt here, but when I saw the code I remembered my days
>as a student where they forced us to do similar things Java :)
>There you tend to make things contained, everything is a class, getters,
>setters and whatnot. In kernel, this is overkill.
>
>I'm not saying it's functionally wrong. What I say is that it is
>completely unnecessary. I see no advantage, by having this indirection.
>I see only disadvantages. It makes code harder to read and follow.
>What I suggest, again, is to make things nice and simple. Set of ops
>that the driver implements for dpll commands or parts of commands,
>as we are used to in the rest of the kernel.
>

No problem, I think we will get rid of it, see comment below.

>
>>As you said, seems a bit revolutionary, but IMHO it simplifies stuff, and
>>basically it is value and validity bit, which I believe is rather common
>in the
>>kernel, this differs only with the fact it is encapsulated. No direct
>access to
>>the fields of structure is available for the users.
>
>I don't see any reason for any validity bits whan you just do it using
>driver-implemented ops.
>
>
>>Most probably there are some things that could be improved with it, but in
>>general it is very easy to use and understand how it works.
>>What could be improved:
>>- naming scheme as function names are a bit long right now, although
>mostly
>>still fits the line-char limits, thus not that problematic
>>- bit mask values are capable of storing 32 bits and bit(0) is always used
>as
>>unspec, which ends up with 31 values available for the enums so if by any
>>chance one of the attribute enums would go over 32 it could be an issue.
>>
>>It is especially useful for multiple values passed with the same netlink
>>attribute id. I.e. please take a look at
>dpll_msg_add_pin_types_supported(..)
>>function.
>>
>>>   Please keep things direct and simple:
>>>   * If some option could be changed for a pin or dpll, just have an
>>>     op that is directly called from netlink handler to change it.
>>>     There should be clear set of ops for configuration of pin and
>>>     dpll object. This "attr" indirection make this totally invisible.
>>
>>In last review you have asked to have rather only set and get ops defined
>>with a single attribute struct. This is exactly that, altough
>encapsulated.
>
>For objects, yes. Pass a struct you directly read/write if the amount of
>function args would be bigger then say 4. The whole encapsulation is not
>good for anything.

If there is one set/get for whole object, then a writer of a struct (netlink
or driver) has to have possibility to indicate which parts of the struct were
actually set, so a reader can do things that were requested.

But I agree this is probably not any better to the "ops per attribute" approach
we have had before, thus I think we shall get back to this approach and remove
dpll_attr/dpll_pin_attr entirely.

>
>
>>
>>>   * If some attribute is const during dpll or pin lifetime, have it
>>>     passed during dpll or pin creation.
>>>
>>>
>>
>>Only driver knows which attributes are const and which are not, this shall
>
>Nonono. This is semantics defined by the subsystem. The pin
>label/description for example. That is const, cannot be set by the user.

True, it is const and it is passed on pin init.

>The type of the pin (synce/gnss/ext) is const, cannot be set by the user.

It can, as input/source can change, thus the same way, type of pin could be
managed by some hardware fuses/switches/etc. 

>This you have to clearly specify when you define driver API.
>This const attrs should be passed during pin creation/registration.
>
>Talking about dpll instance itself, the clock_id, clock_quality, these
>should be also const attrs.
>

Actually, clock_quality can also vary on runtime (i.e. ext/synce). We cannot
determine what Quality Level signal user has connected to the SMA or was
received from the network. Only gnss/oscilattor could have const depending
on used HW. But generally it shall not be const.

Thanks,
Arkadiusz

>
>
>>be also part of driver implementation.
>>As I understand all the fields present in (dpll/dpll_pin)_attr, used in
>get/set
>>ops, could be altered in run-time depending on HW design.
>>
>>Thanks,
>>Arkadiusz
>>
>>>
>>>>
>>>>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 (1):
>>>>  dpll: add dpll_attr/dpll_pin_attr helper classes
>>>>
>>>>Vadim Fedorenko (3):
>>>>  dpll: Add DPLL framework base functions
>>>>  dpll: documentation on DPLL subsystem interface
>>>>  ptp_ocp: implement DPLL ops
>>>>
>>>> Documentation/networking/dpll.rst  | 271 ++++++++
>>>> Documentation/networking/index.rst |   1 +
>>>> MAINTAINERS                        |   8 +
>>>> drivers/Kconfig                    |   2 +
>>>> drivers/Makefile                   |   1 +
>>>> drivers/dpll/Kconfig               |   7 +
>>>> drivers/dpll/Makefile              |  11 +
>>>> drivers/dpll/dpll_attr.c           | 278 +++++++++
>>>> drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
>>>> drivers/dpll/dpll_core.h           | 176 ++++++
>>>> drivers/dpll/dpll_netlink.c        | 963 +++++++++++++++++++++++++++++
>>>> drivers/dpll/dpll_netlink.h        |  24 +
>>>> drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
>>>> drivers/ptp/Kconfig                |   1 +
>>>> drivers/ptp/ptp_ocp.c              | 123 ++--
>>>> include/linux/dpll.h               | 261 ++++++++
>>>> include/linux/dpll_attr.h          | 433 +++++++++++++
>>>> include/uapi/linux/dpll.h          | 263 ++++++++
>>>> 18 files changed, 4002 insertions(+), 37 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_attr.c 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/dpll/dpll_pin_attr.c create mode 100644 include/linux/dpll.h
>>>> create mode 100644 include/linux/dpll_attr.h create mode 100644
>>>> include/uapi/linux/dpll.h
>>>>
>>>>--
>>>>2.27.0
>>>>

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

* RE: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-02 16:20           ` Jiri Pirko
@ 2022-12-08  0:35             ` Kubalewski, Arkadiusz
  2022-12-08  8:19               ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-08  0:35 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Friday, December 2, 2022 5:21 PM
>
>Fri, Dec 02, 2022 at 03:39:17PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, December 2, 2022 1:49 PM
>>>
>>>Fri, Dec 02, 2022 at 12:27:32PM CET, arkadiusz.kubalewski@intel.com
>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Wednesday, November 30, 2022 1:41 PM
>>>>>
>>>>>Tue, Nov 29, 2022 at 10:37:24PM CET, vfedorenko@novek.ru wrote:
>>>>>>From: Vadim Fedorenko <vadfed@fb.com>
>>>
>>>[...]
>>>
>>>
>>>>>>+static int ptp_ocp_dpll_get_attr(struct dpll_device *dpll, struct
>>>>>dpll_attr *attr)
>>>>>>+{
>>>>>>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>>>>>>+	int sync;
>>>>>>+
>>>>>>+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>>>>>>+	dpll_attr_lock_status_set(attr, sync ? DPLL_LOCK_STATUS_LOCKED
>:
>>>>>DPLL_LOCK_STATUS_UNLOCKED);
>>>>>
>>>>>get,set,confuse. This attr thing sucks, sorry :/
>>>>
>>>>Once again, I feel obligated to add some explanations :)
>>>>
>>>>getter is ops called by dpll subsystem, it requires data, so here value
>>>>shall be set for the caller, right?
>>>>Also have explained the reason why this attr struct and functions are
>>>>done this way in the response to cover letter concerns.
>>>
>>>Okay, I will react there.
>>
>>Thanks!
>>
>>>
>>>
>>>>
>>>>>
>>>>>
>>>>>>+
>>>>>>+	return 0;
>>>>>>+}
>>>>>>+
>>>>>>+static int ptp_ocp_dpll_pin_get_attr(struct dpll_device *dpll,
>>>>>>+struct
>>>>>dpll_pin *pin,
>>>>>>+				     struct dpll_pin_attr *attr) {
>>>>>>+	dpll_pin_attr_type_set(attr, DPLL_PIN_TYPE_EXT);
>>>>>
>>>>>This is exactly what I was talking about in the cover letter. This is
>>>>>const, should be put into static struct and passed to
>>>>>dpll_device_alloc().
>>>>
>>>>Actually this type or some other parameters might change in the
>>>>run-time,
>>>
>>>No. This should not change.
>>>If the pin is SyncE port, it's that for all lifetime of pin. It cannot
>turn
>>>to be a EXT/SMA connector all of the sudden. This should be definitelly
>>>fixed, it's a device topology.
>>>
>>>Can you explain the exact scenario when the change of personality of pin
>>>can happen? Perhaps I'm missing something.
>>>
>>
>>Our device is not capable of doing this type of switch, but why to assume
>>that some other HW would not? As I understand generic dpll subsystem must
>not
>>be tied to any HW, and you proposal makes it exactly tied to our
>approaches.
>>As Vadim requested to have possibility to change pin between source/output
>>"states" this seems also possible that some HW might have multiple types
>>possible.
>
>How? How do you physically change from EXT connector to SyncE port? That
>does not make sense. Topology is given. Let's go back to Earth here.
>

I suppose by using some kind of hardware fuse/signal selector controlled by
firmware/driver. Don't think it is out of space, just depends on hardware.

>
>>I don't get why "all of the sudden", DPLLA_PIN_TYPE_SUPPORTED can have
>multiple
>>values, which means that the user can pick one of those with set command.
>>Then if HW supports it could redirect signals/setup things accordingly.
>
>We have to stritly distinguis between things that are given, wired-up,
>static and things that could be configured.
>

This is supposed to be generic interface, right?
What you insist on, is to hardcode most of it in software, which means that
hardware designs would have to follow possibilities given by the software.

>
>>
>>>
>>>
>>>>depends on the device, it is up to the driver how it will handle any
>>>>getter, if driver knows it won't change it could also have some static
>>>>member and copy the data with: dpll_pin_attr_copy(...);
>>>>
>>>>>
>>>>>
>>>>>>+	return 0;
>>>>>>+}
>>>>>>+
>>>>>>+static struct dpll_device_ops dpll_ops = {
>>>>>>+	.get	= ptp_ocp_dpll_get_attr,
>>>>>>+};
>>>>>>+
>>>>>>+static struct dpll_pin_ops dpll_pin_ops = {
>>>>>>+	.get	= ptp_ocp_dpll_pin_get_attr,
>>>>>>+};
>>>>>>+
>>>>>> static int
>>>>>> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>>>>>> {
>>>>>>+	const u8 dpll_cookie[DPLL_COOKIE_LEN] = { "OCP" };
>>>>>>+	char pin_desc[PIN_DESC_LEN];
>>>>>> 	struct devlink *devlink;
>>>>>>+	struct dpll_pin *pin;
>>>>>> 	struct ptp_ocp *bp;
>>>>>>-	int err;
>>>>>>+	int err, i;
>>>>>>
>>>>>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp),
>&pdev-
>>>>>>dev);
>>>>>> 	if (!devlink) {
>>>>>>@@ -4230,6 +4263,20 @@ ptp_ocp_probe(struct pci_dev *pdev, const
>>>>>>struct
>>>>>pci_device_id *id)
>>>>>>
>>>>>> 	ptp_ocp_info(bp);
>>>>>> 	devlink_register(devlink);
>>>>>>+
>>>>>>+	bp->dpll = dpll_device_alloc(&dpll_ops, DPLL_TYPE_PPS,
>dpll_cookie,
>>>>>pdev->bus->number, bp, &pdev->dev);
>>>>>>+	if (!bp->dpll) {
>>>>>>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>>>>>>+		goto out;
>>>>>>+	}
>>>>>>+	dpll_device_register(bp->dpll);
>>>>>
>>>>>You still have the 2 step init process. I believe it would be better
>>>>>to just have dpll_device_create/destroy() to do it in one shot.
>>>>
>>>>For me either is ok, but due to pins alloc/register as explained below
>>>>I would leave it as it is.
>>>
>>>Please don't, it has no value. Just adds unnecesary code. Have it nice
>and
>>>simple.
>>>
>>
>>Actually this comment relates to the other commit, could we keep comments
>>in the threads they belong to please, this would be much easier to track.
>>But yeah sure, if there is no strong opinion on that we could change it.
>
>Ok.
>
>
>>
>>>
>>>>
>>>>>
>>>>>
>>>>>>+
>>>>>>+	for (i = 0; i < 4; i++) {
>>>>>>+		snprintf(pin_desc, PIN_DESC_LEN, "sma%d", i + 1);
>>>>>>+		pin = dpll_pin_alloc(pin_desc, PIN_DESC_LEN);
>>>>>>+		dpll_pin_register(bp->dpll, pin, &dpll_pin_ops, bp);
>>>>>
>>>>>Same here, no point of having 2 step init.
>>>>
>>>>The alloc of a pin is not required if the pin already exist and would
>>>>be just registered with another dpll.
>>>
>>>Please don't. Have a pin created on a single DPLL. Why you make things
>>>compitated here? I don't follow.
>>
>>Tried to explain on the cover-letter thread, let's discuss there please.
>
>Ok.
>
>
>>
>>>
>>>
>>>>Once we decide to entirely drop shared pins idea this could be probably
>>>>done, although other kernel code usually use this twostep approach?
>>>
>>>No, it does not. It's is used whatever fits on the individual usecase.
>>
>>Similar to above, no strong opinion here from me, for shared pin it is
>>certainly useful.
>>
>>>
>>>
>>>>
>>>>>
>>>>>
>>>>>>+	}
>>>>>>+
>>>>>> 	return 0;
>>>>>
>>>>>
>>>>>Btw, did you consider having dpll instance here as and auxdev? It
>>>>>would be suitable I believe. It is quite simple to do it. See
>>>>>following patch as an example:
>>>>
>>>>I haven't think about it, definetly gonna take a look to see if there
>>>>any benefits in ice.
>>>
>>>Please do. The proper separation and bus/device modelling is at least one
>>>of the benefits. The other one is that all dpll drivers would happily
>live
>>>in drivers/dpll/ side by side.
>>>
>>
>>Well, makes sense, but still need to take a closer look on that.
>>I could do that on ice-driver part, don't feel strong enough yet to
>introduce
>
>Sure Ice should be ready.
>
>
>>Changes here in ptp_ocp.
>
>I think that Vadim said he is going to look at that during the call. My
>commit introducing this to mlxsw is a nice and simple example how this
>could be done in ptp_ocp.
>

Yes, though first need to find a bit of time for it :S

Thank you,
Arkadiusz

>
>>
>>Thank you,
>>Arkadiusz
>>
>>>
>>>
>>>>
>>>>Thanks,
>>>>Arkadiusz
>>>>
>>>>>
>>>>>commit bd02fd76d1909637c95e8ef13e7fd1e748af910d
>>>>>Author: Jiri Pirko <jiri@nvidia.com>
>>>>>Date:   Mon Jul 25 10:29:17 2022 +0200
>>>>>
>>>>>    mlxsw: core_linecards: Introduce per line card auxiliary device
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>>
>>>>>> out:
>>>>>>@@ -4247,6 +4294,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>>>>>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>>>>>> 	struct devlink *devlink = priv_to_devlink(bp);
>>>>>>
>>>>>>+	dpll_device_unregister(bp->dpll);
>>>>>>+	dpll_device_free(bp->dpll);
>>>>>> 	devlink_unregister(devlink);
>>>>>> 	ptp_ocp_detach(bp);
>>>>>> 	pci_disable_device(pdev);
>>>>>>--
>>>>>>2.27.0
>>>>>>

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-07 16:59                         ` Jakub Kicinski
@ 2022-12-08  8:14                           ` Jiri Pirko
  2022-12-08 16:19                             ` Jakub Kicinski
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-08  8:14 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

Wed, Dec 07, 2022 at 05:59:41PM CET, kuba@kernel.org wrote:
>On Wed, 7 Dec 2022 14:10:42 +0100 Jiri Pirko wrote:
>> >> Why do we need this association at all?  
>> >
>> >Someone someday may want netns delegation and if we don't have the
>> >support from the start we may break backward compat introducing it.  
>> 
>> Hmm. Can you imagine a usecase?
>
>Running DPLL control in a namespace / container.
>
>I mean - I generally think netns is overused, but yes, it's what
>containers use, so I think someone may want to develop their
>timer controller SW in as a container?

The netdevices to control are already in the container. Isn't that
enough?


>
>> Link to devlink instance btw might be a problem. In case of mlx5, one
>> dpll instance is going to be created for 2 (or more) PFs. 1 per ConnectX
>> ASIC as there is only 1 clock there. And PF devlinks can come and go,
>> does not make sense to link it to any of them.
>
>If only we stuck to the "one devlink instance per ASIC", huh? :)

Yeah...


>
>> Thinking about it a bit more, DPLL itself has no network notion. The
>> special case is SyncE pin, which is linked to netdevice. Just a small
>> part of dpll device. And the netdevice already has notion of netns.
>> Isn't that enough?
>
>So we can't use devlink or netdev. Hm. So what do we do?
>Make DPLLs only visible in init_net? And require init_net admin?
>And when someone comes asking we add an explicit "move to netns"
>command to DPLL?

Well, as I wrote. The only part needed to be network namespaced are the
netdev related pins. And netdevices have netns support. So my question
again, why is that not enough?


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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-08  0:35             ` Kubalewski, Arkadiusz
@ 2022-12-08  8:19               ` Jiri Pirko
  0 siblings, 0 replies; 87+ messages in thread
From: Jiri Pirko @ 2022-12-08  8:19 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

Thu, Dec 08, 2022 at 01:35:02AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, December 2, 2022 5:21 PM
>>
>>Fri, Dec 02, 2022 at 03:39:17PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, December 2, 2022 1:49 PM
>>>>
>>>>Fri, Dec 02, 2022 at 12:27:32PM CET, arkadiusz.kubalewski@intel.com
>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Wednesday, November 30, 2022 1:41 PM
>>>>>>
>>>>>>Tue, Nov 29, 2022 at 10:37:24PM CET, vfedorenko@novek.ru wrote:
>>>>>>>From: Vadim Fedorenko <vadfed@fb.com>
>>>>
>>>>[...]
>>>>
>>>>
>>>>>>>+static int ptp_ocp_dpll_get_attr(struct dpll_device *dpll, struct
>>>>>>dpll_attr *attr)
>>>>>>>+{
>>>>>>>+	struct ptp_ocp *bp = (struct ptp_ocp *)dpll_priv(dpll);
>>>>>>>+	int sync;
>>>>>>>+
>>>>>>>+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>>>>>>>+	dpll_attr_lock_status_set(attr, sync ? DPLL_LOCK_STATUS_LOCKED
>>:
>>>>>>DPLL_LOCK_STATUS_UNLOCKED);
>>>>>>
>>>>>>get,set,confuse. This attr thing sucks, sorry :/
>>>>>
>>>>>Once again, I feel obligated to add some explanations :)
>>>>>
>>>>>getter is ops called by dpll subsystem, it requires data, so here value
>>>>>shall be set for the caller, right?
>>>>>Also have explained the reason why this attr struct and functions are
>>>>>done this way in the response to cover letter concerns.
>>>>
>>>>Okay, I will react there.
>>>
>>>Thanks!
>>>
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>>+
>>>>>>>+	return 0;
>>>>>>>+}
>>>>>>>+
>>>>>>>+static int ptp_ocp_dpll_pin_get_attr(struct dpll_device *dpll,
>>>>>>>+struct
>>>>>>dpll_pin *pin,
>>>>>>>+				     struct dpll_pin_attr *attr) {
>>>>>>>+	dpll_pin_attr_type_set(attr, DPLL_PIN_TYPE_EXT);
>>>>>>
>>>>>>This is exactly what I was talking about in the cover letter. This is
>>>>>>const, should be put into static struct and passed to
>>>>>>dpll_device_alloc().
>>>>>
>>>>>Actually this type or some other parameters might change in the
>>>>>run-time,
>>>>
>>>>No. This should not change.
>>>>If the pin is SyncE port, it's that for all lifetime of pin. It cannot
>>turn
>>>>to be a EXT/SMA connector all of the sudden. This should be definitelly
>>>>fixed, it's a device topology.
>>>>
>>>>Can you explain the exact scenario when the change of personality of pin
>>>>can happen? Perhaps I'm missing something.
>>>>
>>>
>>>Our device is not capable of doing this type of switch, but why to assume
>>>that some other HW would not? As I understand generic dpll subsystem must
>>not
>>>be tied to any HW, and you proposal makes it exactly tied to our
>>approaches.
>>>As Vadim requested to have possibility to change pin between source/output
>>>"states" this seems also possible that some HW might have multiple types
>>>possible.
>>
>>How? How do you physically change from EXT connector to SyncE port? That
>>does not make sense. Topology is given. Let's go back to Earth here.
>>
>
>I suppose by using some kind of hardware fuse/signal selector controlled by
>firmware/driver. Don't think it is out of space, just depends on hardware.

Can you describe this in more details please? I still don't follow how
it makes sense to allow user to for example change EXT connector to
SyncE port. If the pins are muxed, we already have model for that. But
the same pin physical type change? How?


>
>>
>>>I don't get why "all of the sudden", DPLLA_PIN_TYPE_SUPPORTED can have
>>multiple
>>>values, which means that the user can pick one of those with set command.
>>>Then if HW supports it could redirect signals/setup things accordingly.
>>
>>We have to stritly distinguis between things that are given, wired-up,
>>static and things that could be configured.
>>
>
>This is supposed to be generic interface, right?
>What you insist on, is to hardcode most of it in software, which means that
>hardware designs would have to follow possibilities given by the software.

Sure it is generic. I don't want to hardcode anything. Just the driver
exposes whatever is in HW. If something can change in HW using
configuration, driver/UAPI can expose it. However, what's the point of
exposing something in UAPI which is static in HW? It only introduces
confusion for the UAPI consumer.



>
>>
>>>
>>>>
>>>>
>>>>>depends on the device, it is up to the driver how it will handle any
>>>>>getter, if driver knows it won't change it could also have some static
>>>>>member and copy the data with: dpll_pin_attr_copy(...);
>>>>>
>>>>>>
>>>>>>
>>>>>>>+	return 0;
>>>>>>>+}
>>>>>>>+
>>>>>>>+static struct dpll_device_ops dpll_ops = {
>>>>>>>+	.get	= ptp_ocp_dpll_get_attr,
>>>>>>>+};
>>>>>>>+
>>>>>>>+static struct dpll_pin_ops dpll_pin_ops = {
>>>>>>>+	.get	= ptp_ocp_dpll_pin_get_attr,
>>>>>>>+};
>>>>>>>+
>>>>>>> static int
>>>>>>> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>>>>>>> {
>>>>>>>+	const u8 dpll_cookie[DPLL_COOKIE_LEN] = { "OCP" };
>>>>>>>+	char pin_desc[PIN_DESC_LEN];
>>>>>>> 	struct devlink *devlink;
>>>>>>>+	struct dpll_pin *pin;
>>>>>>> 	struct ptp_ocp *bp;
>>>>>>>-	int err;
>>>>>>>+	int err, i;
>>>>>>>
>>>>>>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp),
>>&pdev-
>>>>>>>dev);
>>>>>>> 	if (!devlink) {
>>>>>>>@@ -4230,6 +4263,20 @@ ptp_ocp_probe(struct pci_dev *pdev, const
>>>>>>>struct
>>>>>>pci_device_id *id)
>>>>>>>
>>>>>>> 	ptp_ocp_info(bp);
>>>>>>> 	devlink_register(devlink);
>>>>>>>+
>>>>>>>+	bp->dpll = dpll_device_alloc(&dpll_ops, DPLL_TYPE_PPS,
>>dpll_cookie,
>>>>>>pdev->bus->number, bp, &pdev->dev);
>>>>>>>+	if (!bp->dpll) {
>>>>>>>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>>>>>>>+		goto out;
>>>>>>>+	}
>>>>>>>+	dpll_device_register(bp->dpll);
>>>>>>
>>>>>>You still have the 2 step init process. I believe it would be better
>>>>>>to just have dpll_device_create/destroy() to do it in one shot.
>>>>>
>>>>>For me either is ok, but due to pins alloc/register as explained below
>>>>>I would leave it as it is.
>>>>
>>>>Please don't, it has no value. Just adds unnecesary code. Have it nice
>>and
>>>>simple.
>>>>
>>>
>>>Actually this comment relates to the other commit, could we keep comments
>>>in the threads they belong to please, this would be much easier to track.
>>>But yeah sure, if there is no strong opinion on that we could change it.
>>
>>Ok.
>>
>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>>+
>>>>>>>+	for (i = 0; i < 4; i++) {
>>>>>>>+		snprintf(pin_desc, PIN_DESC_LEN, "sma%d", i + 1);
>>>>>>>+		pin = dpll_pin_alloc(pin_desc, PIN_DESC_LEN);
>>>>>>>+		dpll_pin_register(bp->dpll, pin, &dpll_pin_ops, bp);
>>>>>>
>>>>>>Same here, no point of having 2 step init.
>>>>>
>>>>>The alloc of a pin is not required if the pin already exist and would
>>>>>be just registered with another dpll.
>>>>
>>>>Please don't. Have a pin created on a single DPLL. Why you make things
>>>>compitated here? I don't follow.
>>>
>>>Tried to explain on the cover-letter thread, let's discuss there please.
>>
>>Ok.
>>
>>
>>>
>>>>
>>>>
>>>>>Once we decide to entirely drop shared pins idea this could be probably
>>>>>done, although other kernel code usually use this twostep approach?
>>>>
>>>>No, it does not. It's is used whatever fits on the individual usecase.
>>>
>>>Similar to above, no strong opinion here from me, for shared pin it is
>>>certainly useful.
>>>
>>>>
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>>+	}
>>>>>>>+
>>>>>>> 	return 0;
>>>>>>
>>>>>>
>>>>>>Btw, did you consider having dpll instance here as and auxdev? It
>>>>>>would be suitable I believe. It is quite simple to do it. See
>>>>>>following patch as an example:
>>>>>
>>>>>I haven't think about it, definetly gonna take a look to see if there
>>>>>any benefits in ice.
>>>>
>>>>Please do. The proper separation and bus/device modelling is at least one
>>>>of the benefits. The other one is that all dpll drivers would happily
>>live
>>>>in drivers/dpll/ side by side.
>>>>
>>>
>>>Well, makes sense, but still need to take a closer look on that.
>>>I could do that on ice-driver part, don't feel strong enough yet to
>>introduce
>>
>>Sure Ice should be ready.
>>
>>
>>>Changes here in ptp_ocp.
>>
>>I think that Vadim said he is going to look at that during the call. My
>>commit introducing this to mlxsw is a nice and simple example how this
>>could be done in ptp_ocp.
>>
>
>Yes, though first need to find a bit of time for it :S
>
>Thank you,
>Arkadiusz
>
>>
>>>
>>>Thank you,
>>>Arkadiusz
>>>
>>>>
>>>>
>>>>>
>>>>>Thanks,
>>>>>Arkadiusz
>>>>>
>>>>>>
>>>>>>commit bd02fd76d1909637c95e8ef13e7fd1e748af910d
>>>>>>Author: Jiri Pirko <jiri@nvidia.com>
>>>>>>Date:   Mon Jul 25 10:29:17 2022 +0200
>>>>>>
>>>>>>    mlxsw: core_linecards: Introduce per line card auxiliary device
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> out:
>>>>>>>@@ -4247,6 +4294,8 @@ ptp_ocp_remove(struct pci_dev *pdev)
>>>>>>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>>>>>>> 	struct devlink *devlink = priv_to_devlink(bp);
>>>>>>>
>>>>>>>+	dpll_device_unregister(bp->dpll);
>>>>>>>+	dpll_device_free(bp->dpll);
>>>>>>> 	devlink_unregister(devlink);
>>>>>>> 	ptp_ocp_detach(bp);
>>>>>>> 	pci_disable_device(pdev);
>>>>>>>--
>>>>>>>2.27.0
>>>>>>>

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
       [not found]               ` <20221207090524.3f562eeb@kernel.org>
@ 2022-12-08 11:22                 ` Jiri Pirko
  2022-12-09  0:36                   ` Jakub Kicinski
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-08 11:22 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-clk

Wed, Dec 07, 2022 at 06:05:24PM CET, kuba@kernel.org wrote:
>On Wed, 7 Dec 2022 14:19:22 +0100 Jiri Pirko wrote:
>>> FWIW auxdev makes absolutely no sense to me for DPLL :/
>>> So Jiri, please say why.
>>
>> Why not? It's a subdev of a device. In mlx5, we have separate auxdevs
>> for eth, rdma, vnet, representors. DPLL is also a separate entity which
>> could be instantiated independently (as it is not really dependent on
>> eth/rdma/etc)). Auxdev looks like an awesome fit. Why do you think it is
>> not?
>>
>> Also, what's good about auxdev is that you can maintain them quite
>> independetly. So there is going to be driver/dpll/ directory which would
>> contain all dpll drivers.
>
>To what practical benefit? Where do we draw the line? Do you want
>PTP clocks to also be auxdevs? DPLL lives in netdev, we don't have
>to complicate things. auxdev is a Conway's law solution.

Auxdev infra is quite simple to implement, I'm not sure what do you mean
by complicating thing here.


>
>mlx5 already looks like sausage meat, it's already minced so you can
>fit it there quite easily, but don't force it on non-enterprise devices.

Not forcing, just suggesting. It's a low-hanging fruit, why not reach
it?

>
>There is non 1:1 relationship with a bus device and subsystem in Linux,
>LMK when you convinced Greg otherwise.

Sure there is not. But maybe that is due to the simple fact that auxdev
was introduces, what, 2 years back? My point is, we are introducing new
subsystem, wouldn't it be nice to start it clean?

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-07 23:21           ` Jakub Kicinski
@ 2022-12-08 11:28             ` Jiri Pirko
  2022-12-09  0:39               ` Jakub Kicinski
  2022-12-08 18:08             ` Maciek Machnikowski
  2022-12-09  0:46             ` Kubalewski, Arkadiusz
  2 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-08 11:28 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev.dump, 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

Thu, Dec 08, 2022 at 12:21:57AM CET, kuba@kernel.org wrote:
>On Wed, 7 Dec 2022 15:09:03 +0100 netdev.dump@gmail.com wrote:
>> > -----Original Message-----
>> > From: Jakub Kicinski <kuba@kernel.org>
>> > Sent: Wednesday, December 7, 2022 3:48 AM
>> > Subject: Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
>> > 
>> > On Fri, 2 Dec 2022 17:12:06 +0100 Jiri Pirko wrote:  
>>  [...]  
>> capable
>>  [...]  
>> require
>>  [...]  
>
>Please fix line wrapping in your email client. 
>And add a name to your account configuration :/
>
>> > > Yep, you have the knowledge of sharing inside the driver, so you should
>> > > do it there. For multiple instances, use in-driver notifier for example.  
>> > 
>> > No, complexity in the drivers is not a good idea. The core should cover
>> > the complexity and let the drivers be simple.  
>> 
>> But how does Driver A know where to connect its pin to? It makes sense to
>> share 
>
>I think we discussed using serial numbers.

Can you remind it? Do you mean serial number of pin?


>
>> pins between the DPLLs exposed by a single driver, but not really outside of
>> it.
>> And that can be done simply by putting the pin ptr from the DPLLA into the
>> pin
>> list of DPLLB.
>
>Are you saying within the driver it's somehow easier? The driver state
>is mostly per bus device, so I don't see how.

You can have some shared data for multiple instances in the driver code,
why not?


>
>> If we want the kitchen-and-sink solution, we need to think about corner
>> cases.
>> Which pin should the API give to the userspace app - original, or
>> muxed/parent?
>
>IDK if I parse but I think both. If selected pin is not directly
>attached the core should configure muxes.
>
>> How would a teardown look like - if Driver A registered DPLLA with Pin1 and
>> Driver B added the muxed pin then how should Driver A properly
>> release its pins? Should it just send a message to driver B and trust that
>> it
>> will receive it in time before we tear everything apart?
>
>Trivial.
>
>> There are many problems with that approach, and the submitted patch is not
>> explaining any of them. E.g. it contains the dpll_muxed_pin_register but no
>> free 
>> counterpart + no flows.
>
>SMOC.

Care to spell this out. I guess you didn't mean "South Middlesex
Opportunity Council" :D


>
>> If we want to get shared pins, we need a good example of how this mechanism
>> can be used.
>
>Agreed.
>
>> > > There are currently 3 drivers for dpll I know of. This in ptp_ocp and
>> > > mlx5 there is no concept of sharing pins. You you are talking about a
>> > > single driver.
>> > >
>> > > What I'm trying to say is, looking at the code, the pin sharing,
>> > > references and locking makes things uncomfortably complex. You are so
>> > > far the only driver to need this, do it internally. If in the future
>> > > other driver appears, this code would be eventually pushed into dpll
>> > > core. No impact on UAPI from what I see. Please keep things as simple as
>> > > possible.  
>> > 
>> > But the pin is shared for one driver. Who cares if it's not shared in
>> > another. The user space must be able to reason about the constraints.
>> > 
>> > You are suggesting drivers to magically flip state in core objects
>> > because of some hidden dependencies?!
>> 
>> If we want to go outside the device, we'd need some universal language
>> to describe external connections - such as the devicetree. I don't see how
>> we can reliably implement inter-driver dependency otherwise.
>
>There's plenty examples in the tree. If we can't use serial number
>directly we can compare the driver pointer + whatever you'd compare
>in the driver internal solution.
>
>> I think this would be better served in the userspace with a board-specific
>> config file. Especially since the pins can be externally connected anyway.
>
>Opinions vary, progress is not being made.

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-08  0:27       ` Kubalewski, Arkadiusz
@ 2022-12-08 11:58         ` Jiri Pirko
  2022-12-08 23:05           ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-08 11:58 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk

Thu, Dec 08, 2022 at 01:27:42AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Friday, December 2, 2022 5:12 PM
>>
>>Fri, Dec 02, 2022 at 12:27:24PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Wednesday, November 30, 2022 1:32 PM
>>>>
>>>>Tue, Nov 29, 2022 at 10:37:20PM CET, vfedorenko@novek.ru wrote:
>>>>>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.
>>>>
>>>>Overall, I see a lot of issues on multiple levels. I will go over them in
>>>>follow-up emails. So far, after couple of hours looking trought this, I
>>>>have following general feelings/notes:
>>>
>>>Hi Jiri,
>>>
>>>As we have been participating in last version, feel obligated to answer to
>>>the concerns.
>>
>>Cool.
>>
>>
>>>
>>>>
>>>>1) Netlink interface looks much saner than in previous versions. I will
>>>>   send couple of notes, mainly around events and object mixtures and
>>>>   couple of bugs/redundancies. But overall, looks fineish.
>>>>
>>>>2) I don't like that concept of a shared pin, at all. It makes things
>>>>   unnecessary complicated. Just have a pin created for dpll instance
>>>>   and that's it. If another instance has the same pin, it should create
>>>>   it as well. Keeps things separate and easy to model. Let the
>>>>   hw/fw/driver figure out the implementation oddities.
>>>>   Why exactly you keep pushing the shared pin idea? Perhaps I'm missing
>>>>   something crucial.
>>>
>>>
>>>If the user request change on pin#0 of dpll#0, the dpll#0 knows about the
>>>change, it reacts accordingly, and notifies the user the something has
>>changed.
>>>Which is rather simple.
>>>
>>>Now, if the dpll#1 is using the same pin (pin#0 of dpll#0), the
>>complicated
>>>part starts. First we have to assume:
>>>- it was initialized with the same description (as it should, to prevent
>>>confusing the user)
>>>- it was initialized with the same order (this is at least nice to have
>>from
>>>user POV, as pin indices are auto generated), and also in case of multiple
>>pins
>>>being shared it would be best for the user to have exactly the same number
>>of
>>>pins initialized, so they have same indices and initialization order
>>doesn't
>>>introduce additional confusion.
>>>
>>>Thus, one reason of shared pins was to prevent having this assumptions
>>ever.
>>>If the pin is shared, all dplls sharing a pin would have the same
>>description
>>>and pin index for a shared pin out of the box.
>>>
>>>Pin attribute changes
>>>The change on dpll#0 pin#0 impacts also dpll#1 pin#0. Notification about
>>the
>>>change shall be also requested from the driver that handles dpll#1. In
>>such
>>>case the driver has to have some dpll monitoring/notifying mechanics,
>>which at
>>>first doesn't look very hard to do, but most likely only if both dplls are
>>>initialized and managed by a single instance of a driver/firmware.
>>>
>>>If board has 2 dplls but each one is managed by its own firmware/driver
>>>instance. User changes frequency of pin#0 signal, the driver of dpll#0
>>must
>>>also notify driver of dpll#1 that pin#0 frequency has changed, dpll#1
>>reacts on
>>>the change, notifies the user.
>>
>>Right.
>>
>>
>>>But this is only doable with assumption, that the board is internally
>>capable
>>>of such internal board level communication, which in case of separated
>>>firmwares handling multiple dplls might not be the case, or it would
>>require
>>>to have some other sw component feel that gap.
>>
>>Yep, you have the knowledge of sharing inside the driver, so you should
>>do it there. For multiple instances, use in-driver notifier for example.
>>
>
>This can be done right now, if driver doesn't need built-in pin sharing or
>doesn't want it, it doesn't have to use it.
>It was designed this way for a reason, reason of reduced complexity of drivers,
>why should we want to increase complexity of multiple objects instead of having
>it in one place?

Nevermind, don't want to repeat myself. Looks like everyone wants this
pin sharing infra in the core, let's now focus on designing it properly.


>
>>
>>>
>>>For complex boards with multiple dplls/sync channels, multiple ports,
>>>multiple firmware instances, it seems to be complicated to share a pin if
>>>each driver would have own copy and should notify all the other about
>>changes.
>>>
>>>To summarize, that is certainly true, shared pins idea complicates stuff
>>>inside of dpll subsystem.
>>>But at the same time it removes complexity from all the drivers which
>>would use
>>
>>There are currently 3 drivers for dpll I know of. This in ptp_ocp and
>>mlx5 there is no concept of sharing pins. You you are talking about a
>>single driver.
>>
>>What I'm trying to say is, looking at the code, the pin sharing,
>>references and locking makes things uncomfortably complex. You are so
>>far the only driver to need this, do it internally. If in the future
>>other driver appears, this code would be eventually pushed into dpll
>>core. No impact on UAPI from what I see. Please keep things as simple as
>>possible.
>>
>
>The part of interface for drivers is right now 17 functions in total, thus I
>wouldn't call it complicated. References between dpll instances and pins, are
>other part of "increased" complexity, but I wouldn't either call it
>over-complicated, besides it is part of dpll-core. Any interface shouldn't make
>the user to look into the code. For users only documentation shall be important.
>If kernel module developer is able to read header and understand what he needs
>to do for his hardware, whether he want to use shared pins or not, and what
>impact would it have.
>
>Thus real question is, if it is easy enough to use both parts.
>I would say the kernel module part is easy to understand even by looking at
>the function names and their arguments. Proper documentation would clear
>anything that is still not clear.
>
>Netlink part is also moving in a right direction.
>
>>
>>>it and is easier for the userspace due to common identification of pins.
>>
>>By identification, you mean "description" right? I see no problem of 2
>>instances have the same pin "description"/label.
>>
>
>"description"/label and index as they are the same pin. Index is auto-generated
>and depends on initialization order, this makes unnecessary dependency.
>
>>
>>>This solution scales up without any additional complexity in the driver,
>>>and without any need for internal per-board communication channels.
>>>
>>>Not sure if this is good or bad, but with current version, both approaches
>>are
>>>possible, so it pretty much depending on the driver to initialize dplls
>>with
>>>separated pin objects as you have suggested (and take its complexity into
>>>driver) or just share them.
>>>
>>>>
>>>>3) I don't like the concept of muxed pins and hierarchies of pins. Why
>>>>   does user care? If pin is muxed, the rest of the pins related to this
>>>>   one should be in state disabled/disconnected. The user only cares
>>>>   about to see which pins are related to each other. It can be easily
>>>>   exposed by "muxid" like this:
>>>>   pin 1
>>>>   pin 2
>>>>   pin 3 muxid 100
>>>>   pin 4 muxid 100
>>>>   pin 5 muxid 101
>>>>   pin 6 muxid 101
>>>>   In this example pins 3,4 and 5,6 are muxed, therefore the user knows
>>>>   if he connects one, the other one gets disconnected (or will have to
>>>>   disconnect the first one explicitly first).
>>>>
>>>
>>>Currently DPLLA_PIN_PARENT_IDX is doing the same thing as you described,
>>it
>>>groups MUXed pins, the parent pin index here was most straightforward to
>>me,
>>
>>There is a big difference if we model flat list of pins with a set of
>>attributes for each, comparing to a tree of pins, some acting as leaf,
>>node and root. Do we really need such complexicity? What value does it
>>bring to the user to expose this?
>>
>
>If the one doesn't need to use muxed pins, everything is simple, as you have
>described. In fact in the example you have given, only benefit I can see from
>muxid is that user knows which pins would get disconnected when one of the mux
>group pins is selected. But do user really needs that knowledge? If one pin
>was selected why should he look on the other pins?

Right. That would be probably valuable only for output pins.


>
>Anyway I see your point, but this is not only about identifying a muxed pins,
>it is more about identifying connections between them because of hardware
>capabilities they can bring, and utilization of such capabilities.
>Please take a look on a comment below.
>
>>
>>>as in case of DPLL_MODE_AUTOMATIC, where dpll auto-selects highest
>>priority
>>>available signal. The priority can be only assigned to the pins directly
>>>connected to the dpll. The rest of pins (which would have present
>>>attribute DPLLA_PIN_PARENT_IDX) are the ones that require manual selection
>>>even if DPLL_MODE_AUTOMATIC is enabled.
>>>
>>>Enabling a particular pin and sub-pin in DPLL_MODE_AUTOMATIC requires from
>>user
>>>to select proper priority on on a dpll-level MUX-pin and manually select
>>one of
>>>the sub-pins.
>>>On the other hand for DPLL_MODE_FORCED, this might be also beneficial, as
>>the
>>>user could select a directly connected pin and muxed pin with two
>>separated
>>>commands, which could be handled in separated driver instances (if HW
>>design
>>>requires such approach) or either it can be handled just by one select
>>call
>>>for the pin connected directly and handled entirely in the one driver
>>instance.
>>
>>Talk netlink please. What are the actual commands with cmds used to
>>select the source and select a mux pin? You are talking about 2 types of
>>selections:
>>1) Source select
>>   - this is as you described either auto/forces (manual) mode,
>>   according to some prio, dpll select the best source
>>2) Pin select in a mux
>>   - here the pin could be source or output
>>
>>But again, from the user perspective, why does he have to distinguish
>>these? Extending my example:
>>
>>   pin 1 source
>>   pin 2 output
>>   pin 3 muxid 100 source
>>   pin 4 muxid 100 source
>>   pin 5 muxid 101 source
>>   pin 6 muxid 101 source
>>   pin 7 output
>>
>>User now can set individial prios for sources:
>>
>>dpll x pin 1 set prio 10
>>etc
>>The result would be:
>>
>>   pin 1 source prio 10
>>   pin 2 output
>>   pin 3 muxid 100 source prio 8
>>   pin 4 muxid 100 source prio 20
>>   pin 5 muxid 101 source prio 50
>>   pin 6 muxid 101 source prio 60
>>   pin 7 output
>>
>>Now when auto is enabled, the pin 3 is selected. Why would user need to
>>manually select between 3 and 4? This is should be abstracted out by the
>>driver.
>
>Yes, this could be abstracted in the driver, but we are talking here about
>different things. We need automatic hardware-supported selection of a pin,
>not automatic software-supported selection, where driver (part of software
>stack) is doing autoselection.
>
>If dpll is capable of hw-autoselection, the priorities are configured in the
>hardware, not in the driver. This interface is a proxy between user and dpll.

Okay, this is very nice example, where the confusion appeared. Looking
into your code, function dpll_pin_attr_from_nlattr(), it clearly allows
userspace to set the prio over DPLLA_PIN_PRIO. Yet you claim the prio is
configured in the hardware.


>The one could use it as you have described by implementing auto-selection
>in SW, but those are two different modes, actually we could introduce modes
>like: DPLL_MODE_HW_AUTOMATIC and DPLL_MODE_SW_AUTOMATIC, to clear things up.
>Although, IMHO, DPLL_MODE_SW_AUTOMATIC wouldn't really differ from current
>behavior of DPLL_MODE_FORCED, where the userspace is explicitly selecting a
>source, and the only difference would be that selection comes from driver
>instead of userspace tool. Thus in the end it would just increase complexity
>of a driver (instead of bringing any benefits).

Yeah, does not make sense to have DPLL_MODE_SW_AUTOMATIC as you describe
it. I agree.

>
>>
>>Only issues I see when pins are output. User would have to somehow
>>select one of the pins in the mux (perhaps another dpll cmd). But the
>>mux pin instance does not help with anything there...
>>
>>
>>
>>>
>>>>4) I don't like the "attr" indirection. It makes things very tangled. It
>>>>   comes from the concepts of classes and objects and takes it to
>>>>   extreme. Not really something we are commonly used to in kernel.
>>>>   Also, it brings no value from what I can see, only makes things very
>>>>   hard to read and follow.
>>>>
>>>
>>>Yet again, true, I haven't find anything similar in the kernel, it was
>>more
>>>like a try to find out a way to have a single structure with all the stuff
>>that
>>>is passed between netlink/core/driver parts. Came up with this, and to be
>>>honest it suits pretty well, those are well defined containers. They store
>>>attributes that either user or driver have set, with ability to obtain a
>>valid
>>>value only if it was set. Thus whoever reads a struct, knows which of
>>those
>>>attributes were actually set.
>>
>>Sorry for being blunt here, but when I saw the code I remembered my days
>>as a student where they forced us to do similar things Java :)
>>There you tend to make things contained, everything is a class, getters,
>>setters and whatnot. In kernel, this is overkill.
>>
>>I'm not saying it's functionally wrong. What I say is that it is
>>completely unnecessary. I see no advantage, by having this indirection.
>>I see only disadvantages. It makes code harder to read and follow.
>>What I suggest, again, is to make things nice and simple. Set of ops
>>that the driver implements for dpll commands or parts of commands,
>>as we are used to in the rest of the kernel.
>>
>
>No problem, I think we will get rid of it, see comment below.
>
>>
>>>As you said, seems a bit revolutionary, but IMHO it simplifies stuff, and
>>>basically it is value and validity bit, which I believe is rather common
>>in the
>>>kernel, this differs only with the fact it is encapsulated. No direct
>>access to
>>>the fields of structure is available for the users.
>>
>>I don't see any reason for any validity bits whan you just do it using
>>driver-implemented ops.
>>
>>
>>>Most probably there are some things that could be improved with it, but in
>>>general it is very easy to use and understand how it works.
>>>What could be improved:
>>>- naming scheme as function names are a bit long right now, although
>>mostly
>>>still fits the line-char limits, thus not that problematic
>>>- bit mask values are capable of storing 32 bits and bit(0) is always used
>>as
>>>unspec, which ends up with 31 values available for the enums so if by any
>>>chance one of the attribute enums would go over 32 it could be an issue.
>>>
>>>It is especially useful for multiple values passed with the same netlink
>>>attribute id. I.e. please take a look at
>>dpll_msg_add_pin_types_supported(..)
>>>function.
>>>
>>>>   Please keep things direct and simple:
>>>>   * If some option could be changed for a pin or dpll, just have an
>>>>     op that is directly called from netlink handler to change it.
>>>>     There should be clear set of ops for configuration of pin and
>>>>     dpll object. This "attr" indirection make this totally invisible.
>>>
>>>In last review you have asked to have rather only set and get ops defined
>>>with a single attribute struct. This is exactly that, altough
>>encapsulated.
>>
>>For objects, yes. Pass a struct you directly read/write if the amount of
>>function args would be bigger then say 4. The whole encapsulation is not
>>good for anything.
>
>If there is one set/get for whole object, then a writer of a struct (netlink
>or driver) has to have possibility to indicate which parts of the struct were
>actually set, so a reader can do things that were requested.
>
>But I agree this is probably not any better to the "ops per attribute" approach
>we have had before, thus I think we shall get back to this approach and remove
>dpll_attr/dpll_pin_attr entirely.
>
>>
>>
>>>
>>>>   * If some attribute is const during dpll or pin lifetime, have it
>>>>     passed during dpll or pin creation.
>>>>
>>>>
>>>
>>>Only driver knows which attributes are const and which are not, this shall
>>
>>Nonono. This is semantics defined by the subsystem. The pin
>>label/description for example. That is const, cannot be set by the user.
>
>True, it is const and it is passed on pin init.
>
>>The type of the pin (synce/gnss/ext) is const, cannot be set by the user.
>
>It can, as input/source can change, thus the same way, type of pin could be
>managed by some hardware fuses/switches/etc. 

Could you please describe this a bit more? For example how the user can
change synce pin to ext pin? I still fail to understand how such thing
is possible. How the HW could be rewired on user request?
Perhaps we understand differently the "pin" object. I understand is as
something out-facing. Not an actual pin of a chip.


>
>>This you have to clearly specify when you define driver API.
>>This const attrs should be passed during pin creation/registration.
>>
>>Talking about dpll instance itself, the clock_id, clock_quality, these
>>should be also const attrs.
>>
>
>Actually, clock_quality can also vary on runtime (i.e. ext/synce). We cannot
>determine what Quality Level signal user has connected to the SMA or was
>received from the network. Only gnss/oscilattor could have const depending
>on used HW. But generally it shall not be const.

Sec. I'm talkign about the actual dpll quality, means the internal
clock. How it can vary?


>
>Thanks,
>Arkadiusz
>
>>
>>
>>>be also part of driver implementation.
>>>As I understand all the fields present in (dpll/dpll_pin)_attr, used in
>>get/set
>>>ops, could be altered in run-time depending on HW design.
>>>
>>>Thanks,
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>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 (1):
>>>>>  dpll: add dpll_attr/dpll_pin_attr helper classes
>>>>>
>>>>>Vadim Fedorenko (3):
>>>>>  dpll: Add DPLL framework base functions
>>>>>  dpll: documentation on DPLL subsystem interface
>>>>>  ptp_ocp: implement DPLL ops
>>>>>
>>>>> Documentation/networking/dpll.rst  | 271 ++++++++
>>>>> Documentation/networking/index.rst |   1 +
>>>>> MAINTAINERS                        |   8 +
>>>>> drivers/Kconfig                    |   2 +
>>>>> drivers/Makefile                   |   1 +
>>>>> drivers/dpll/Kconfig               |   7 +
>>>>> drivers/dpll/Makefile              |  11 +
>>>>> drivers/dpll/dpll_attr.c           | 278 +++++++++
>>>>> drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
>>>>> drivers/dpll/dpll_core.h           | 176 ++++++
>>>>> drivers/dpll/dpll_netlink.c        | 963 +++++++++++++++++++++++++++++
>>>>> drivers/dpll/dpll_netlink.h        |  24 +
>>>>> drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
>>>>> drivers/ptp/Kconfig                |   1 +
>>>>> drivers/ptp/ptp_ocp.c              | 123 ++--
>>>>> include/linux/dpll.h               | 261 ++++++++
>>>>> include/linux/dpll_attr.h          | 433 +++++++++++++
>>>>> include/uapi/linux/dpll.h          | 263 ++++++++
>>>>> 18 files changed, 4002 insertions(+), 37 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_attr.c 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/dpll/dpll_pin_attr.c create mode 100644 include/linux/dpll.h
>>>>> create mode 100644 include/linux/dpll_attr.h create mode 100644
>>>>> include/uapi/linux/dpll.h
>>>>>
>>>>>--
>>>>>2.27.0
>>>>>

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
       [not found]           ` <20221207091946.3115742f@kernel.org>
@ 2022-12-08 12:02             ` Jiri Pirko
  2022-12-09  0:54               ` Jakub Kicinski
  2022-12-08 18:23             ` Kubalewski, Arkadiusz
  1 sibling, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-08 12:02 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, linux-arm-kernel, linux-clk

Wed, Dec 07, 2022 at 06:19:46PM CET, kuba@kernel.org wrote:
>On Wed, 7 Dec 2022 15:51:42 +0100 Jiri Pirko wrote:
>> Wed, Dec 07, 2022 at 03:47:40AM CET, kuba@kernel.org wrote:
>> >On Fri, 2 Dec 2022 17:12:06 +0100 Jiri Pirko wrote:  
>> >> Yep, you have the knowledge of sharing inside the driver, so you should
>> >> do it there. For multiple instances, use in-driver notifier for example.  
>> >
>> >No, complexity in the drivers is not a good idea. The core should cover
>> >the complexity and let the drivers be simple.  
>> 
>> Really, even in case only one driver actually consumes the complexicity?
>> I understand having a "libs" to do common functionality for drivers,
>> even in case there is one. But this case, I don't see any benefit.
>
>In the same email thread you admit that mlx5 has multiple devlink
>instances for the same ASIC and refuse to try to prevent similar
>situation happening in the new subsystem.

I don't understand your point. In CX there is 1 clock for 2 pci PFs. I
plan to have 1 dpll instance for the clock shared.

But how is what you write relevant to the discussion? We are talking
about:
a) 1 pin in 2 dpll instances
what I undestand you say here is to prevent:
b) 2 dpll instances for 1 clock
apples and oranges. Am I missing something?

I'm totally against b) but that is not what we discuss here, correct?


>
>> >> There are currently 3 drivers for dpll I know of. This in ptp_ocp and
>> >> mlx5 there is no concept of sharing pins. You you are talking about a
>> >> single driver.
>> >> 
>> >> What I'm trying to say is, looking at the code, the pin sharing,
>> >> references and locking makes things uncomfortably complex. You are so
>> >> far the only driver to need this, do it internally. If in the future
>> >> other driver appears, this code would be eventually pushed into dpll
>> >> core. No impact on UAPI from what I see. Please keep things as simple as
>> >> possible.  
>> >
>> >But the pin is shared for one driver. Who cares if it's not shared in
>> >another. The user space must be able to reason about the constraints.  
>> 
>> Sorry, I don't follow :/ Could you please explain what do you mean by
>> this?
>
>We don't wait with adding APIs until there is more than one driver that
>needs them.

Agreed. I was under impression that this is only kernel internals and
won't affect the UAPI. Perhaps I'm wrong.


>
>> >You are suggesting drivers to magically flip state in core objects
>> >because of some hidden dependencies?!  
>> 
>> It's not a state flip. It's more like a well propagated event of a state
>> change. The async changes may happen anyway, so the userspace needs
>> to handle them. Why is this different?
>
>What if the user space wants conflicting configurations for the muxes
>behind a shared pin?
>
>The fact that there is a notification does not solve the problem of
>user space not knowing what's going on. Why would the user space play
>guessing games if the driver _knows_ the topology and can easily tell
>it.

Okay. I get your point. This visibility is probably something nice to
have. If it weights over the added complexicity, I'm not sure. But it
looks like you are, and I don't care that much. So let's focus on
defining the shared pin model properly.


>> >> There is a big difference if we model flat list of pins with a set of
>> >> attributes for each, comparing to a tree of pins, some acting as leaf,
>> >> node and root. Do we really need such complexicity? What value does it
>> >> bring to the user to expose this?  
>> >
>> >The fact that you can't auto select from devices behind muxes.  
>> 
>> Why? What's wrong with the mechanism I described in another part of this
>> thread?
>> 
>> Extending my example from above
>> 
>>    pin 1 source
>>    pin 2 output
>>    pin 3 muxid 100 source
>>    pin 4 muxid 100 source
>>    pin 5 muxid 101 source
>>    pin 6 muxid 101 source
>>    pin 7 output
>> 
>> User now can set individial prios for sources:
>> 
>> dpll x pin 1 set prio 10
>> etc
>> The result would be:
>> 
>>    pin 1 source prio 10
>>    pin 2 output
>>    pin 3 muxid 100 source prio 8
>>    pin 4 muxid 100 source prio 20
>>    pin 5 muxid 101 source prio 50
>>    pin 6 muxid 101 source prio 60
>>    pin 7 output
>> 
>> Now when auto is enabled, the pin 3 is selected. Why would user need to
>> manually select between 3 and 4? This is should be abstracted out by the
>> driver.
>> 
>> Actually, this is neat as you have one cmd to do selection in manual
>> mode and you have uniform way of configuring/monitoring selection in
>> autosel. Would the muxed pin make this better?
>> 
>> For muxed pin being output, only one pin from mux would be set:
>> 
>>    pin 1 source
>>    pin 2 output
>>    pin 3 muxid 100 disconnected
>>    pin 4 muxid 100 disconnected
>>    pin 5 muxid 101 output
>>    pin 6 muxid 101 disconnected
>>    pin 7 output
>
>Sorry, can't parse, could you draw the diagram?

There is no diagram. It's a plain list of pins with attributes, one pin
with attributes per line.


>
>To answer the most basic question - my understanding is that for
>prio-based selection there needs to be silicon that can tell if
>there is a valid clock on the line. While mux is just a fancy switch,
>it has no complex logic, just connects wires.
>
>Arkadiusz, is my understanding incorrect? I may have "intuited" this.
>
>IDK if there are any bidirectional pins after a mux, but that'd be
>another problem. Muxes are only simple for inputs.
>
>> >The HW topology is of material importance to user space.  
>> 
>> Interesting. When I was working on linecards, you said that the user
>> does not care about the inner HW topology. And it makes sense. When
>> things could be abstracted out to make iface clean, I think they should.
>
>I recall only the FW related conversations, but what I think is key 
>is whether the information can be acted upon.

What I was refering to was the device/gearbox exposure per-linecard.

>
>> >How many times does Arkadiusz have to explain this :|  
>> 
>> Pardon my ignorance, I don't see why exactly we need mux hierarchy
>> (trees) exposed to user.

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-08  8:14                           ` Jiri Pirko
@ 2022-12-08 16:19                             ` Jakub Kicinski
  2022-12-08 16:33                               ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-08 16:19 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

On Thu, 8 Dec 2022 09:14:32 +0100 Jiri Pirko wrote:
> >Running DPLL control in a namespace / container.
> >
> >I mean - I generally think netns is overused, but yes, it's what
> >containers use, so I think someone may want to develop their
> >timer controller SW in as a container?  
> 
> The netdevices to control are already in the container. Isn't that
> enough?

For DPLL config we need to delegate the permission.
So we'd need a "is net admin in namespace X" check, no?

> >> Thinking about it a bit more, DPLL itself has no network notion. The
> >> special case is SyncE pin, which is linked to netdevice. Just a small
> >> part of dpll device. And the netdevice already has notion of netns.
> >> Isn't that enough?  
> >
> >So we can't use devlink or netdev. Hm. So what do we do?
> >Make DPLLs only visible in init_net? And require init_net admin?
> >And when someone comes asking we add an explicit "move to netns"
> >command to DPLL?  
> 
> Well, as I wrote. The only part needed to be network namespaced are the
> netdev related pins. And netdevices have netns support. So my question
> again, why is that not enough?

For config which goes thru rtnl, yes, but we also need a caps check for:

+	DPLL_CMD_DEVICE_SET,
+	DPLL_CMD_PIN_SET,

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

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

Thu, Dec 08, 2022 at 05:19:55PM CET, kuba@kernel.org wrote:
>On Thu, 8 Dec 2022 09:14:32 +0100 Jiri Pirko wrote:
>> >Running DPLL control in a namespace / container.
>> >
>> >I mean - I generally think netns is overused, but yes, it's what
>> >containers use, so I think someone may want to develop their
>> >timer controller SW in as a container?  
>> 
>> The netdevices to control are already in the container. Isn't that
>> enough?
>
>For DPLL config we need to delegate the permission.
>So we'd need a "is net admin in namespace X" check, no?

See below.


>
>> >> Thinking about it a bit more, DPLL itself has no network notion. The
>> >> special case is SyncE pin, which is linked to netdevice. Just a small
>> >> part of dpll device. And the netdevice already has notion of netns.
>> >> Isn't that enough?  
>> >
>> >So we can't use devlink or netdev. Hm. So what do we do?
>> >Make DPLLs only visible in init_net? And require init_net admin?
>> >And when someone comes asking we add an explicit "move to netns"
>> >command to DPLL?  
>> 
>> Well, as I wrote. The only part needed to be network namespaced are the
>> netdev related pins. And netdevices have netns support. So my question
>> again, why is that not enough?
>
>For config which goes thru rtnl, yes, but we also need a caps check for:
>
>+	DPLL_CMD_DEVICE_SET,
>+	DPLL_CMD_PIN_SET,

For any synce pin manipulation over dpll netlink, we can use the netns
check of the linked netdev. This is the netns aware leg of the dpll,
it should be checked for.

I can't imagine practically havind the whole dpll instance netns aware.
Omitting the fact that it really has no meaning for non-synce pins, what
would be the behaviour when for example pin 1 is in netns a, pin 2 in
netns b and dpll itself in netns c?

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-08 16:33                               ` Jiri Pirko
@ 2022-12-08 17:05                                 ` Jakub Kicinski
  2022-12-09  9:29                                   ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-08 17:05 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

On Thu, 8 Dec 2022 17:33:28 +0100 Jiri Pirko wrote:
> For any synce pin manipulation over dpll netlink, we can use the netns
> check of the linked netdev. This is the netns aware leg of the dpll,
> it should be checked for.

The OCP card is an atomic clock, it does not have any networking.

> I can't imagine practically havind the whole dpll instance netns aware.
> Omitting the fact that it really has no meaning for non-synce pins, what
> would be the behaviour when for example pin 1 is in netns a, pin 2 in
> netns b and dpll itself in netns c?

To be clear I don't think it's a bad idea in general, I've done 
the same thing for my WIP PSP patches. But we already have one
device without netdevs, hence I thought maybe devlink. So maybe
we do the same thing with devlink? I mean - allow multiple devlink
instances to be linked and require caps on any of them?

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-07 23:21           ` Jakub Kicinski
  2022-12-08 11:28             ` Jiri Pirko
@ 2022-12-08 18:08             ` Maciek Machnikowski
  2022-12-09 11:07               ` Jiri Pirko
  2022-12-09  0:46             ` Kubalewski, Arkadiusz
  2 siblings, 1 reply; 87+ messages in thread
From: Maciek Machnikowski @ 2022-12-08 18:08 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: 'Jiri Pirko', 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

On 12/8/2022 12:21 AM, Jakub Kicinski wrote:
> On Wed, 7 Dec 2022 15:09:03 +0100 netdev.dump@gmail.com wrote:
>>> -----Original Message-----
>>> From: Jakub Kicinski <kuba@kernel.org>
>> pins between the DPLLs exposed by a single driver, but not really outside of
>> it.
>> And that can be done simply by putting the pin ptr from the DPLLA into the
>> pin
>> list of DPLLB.
> 
> Are you saying within the driver it's somehow easier? The driver state
> is mostly per bus device, so I don't see how.
> 
>> If we want the kitchen-and-sink solution, we need to think about corner
>> cases.
>> Which pin should the API give to the userspace app - original, or
>> muxed/parent?
> 
> IDK if I parse but I think both. If selected pin is not directly
> attached the core should configure muxes.
> 
>> How would a teardown look like - if Driver A registered DPLLA with Pin1 and
>> Driver B added the muxed pin then how should Driver A properly
>> release its pins? Should it just send a message to driver B and trust that
>> it
>> will receive it in time before we tear everything apart?
> 
> Trivial.
> 
>> There are many problems with that approach, and the submitted patch is not
>> explaining any of them. E.g. it contains the dpll_muxed_pin_register but no
>> free 
>> counterpart + no flows.
> 
> SMOC.
> 
>> If we want to get shared pins, we need a good example of how this mechanism
>> can be used.
> 
> Agreed.

My main complaint about the current pins implementation is that they put
everything in a single bag. In a netdev world - it would be like we put
TX queues and RX queues together, named them "Queues", expose a list to
the userspace and let the user figure out which ones which by reading a
"TX" flag.

All DPLLs I know have a Sources block, DPLLs and Output blocks. See:

https://www.renesas.com/us/en/products/clocks-timing/jitter-attenuators-frequency-translation/8a34044-multichannel-dpll-dco-four-eight-channels#overview

https://ww1.microchip.com/downloads/aemDocuments/documents/TIM/ProductDocuments/ProductBrief/ZL3063x-System-Synchronizers-with-up-to-5-Channels-10-Inputs-20-Outputs-Product-Brief-DS20006634.pdf

https://www.sitime.com/support/resource-library/product-briefs/cascade-sit9514x-clock-system-chip-family

https://www.ti.com/lit/ds/symlink/lmk5b33414.pdf?ts=1670516132647&ref_url=https%253A%252F%252Fwww.ti.com%252Fclocks-timing%252Fjitter-cleaners-synchronizers%252Fproducts.html

If we model everything as "pins" we won't be able to correctly extend
the API to add new features.

Sources can configure the expected frequency, input signal monitoring
(on multiple layers), expected signal levels, input termination and so
on. Outputs will need the enable flag, signal format, frequency, phase
offset etc. Multiple DPLLs can reuse a single source inside the same
package simultaneously.

A source should be able to link to a pin or directly to the netdev for
some embedded solutions. We don't need to go through the pin abstraction
at all.

An optional pin entity should only represent a physical connection with
a name and maybe a 3-state selection of In/Out/HiZ and then link to
sources or output of the DPLL(s).

Finally, the DPLL object should keep track of the source priority list,
have a proper status (locked/unlocked/holdover/freerunning), implement
the NCO mode, lock thresholds, bandwidths, auto-switch mode and so on.

Current implementation creates a lot of ambiguity, mixes input pins with
output pins and assigns priories to pins. Every SW entity will receive a
big list of pins and will need to parse it.

I prefer the approach that the ptp subsystem set - with its abstraction
of input/output channels and pins that can be assigned to them. While
not perfect - it represents reality much closer.

Thanks
Maciek

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
       [not found]           ` <20221207091946.3115742f@kernel.org>
  2022-12-08 12:02             ` Jiri Pirko
@ 2022-12-08 18:23             ` Kubalewski, Arkadiusz
  1 sibling, 0 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-08 18:23 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko
  Cc: Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, netdev,
	linux-arm-kernel, linux-clk

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Wednesday, December 7, 2022 6:20 PM
>On Wed, 7 Dec 2022 15:51:42 +0100 Jiri Pirko wrote:
>> Wed, Dec 07, 2022 at 03:47:40AM CET, kuba@kernel.org wrote:
>> >On Fri, 2 Dec 2022 17:12:06 +0100 Jiri Pirko wrote:
>> >> Yep, you have the knowledge of sharing inside the driver, so you
>should
>> >> do it there. For multiple instances, use in-driver notifier for
>example.
>> >
>> >No, complexity in the drivers is not a good idea. The core should cover
>> >the complexity and let the drivers be simple.
>>
>> Really, even in case only one driver actually consumes the complexicity?
>> I understand having a "libs" to do common functionality for drivers,
>> even in case there is one. But this case, I don't see any benefit.
>
>In the same email thread you admit that mlx5 has multiple devlink
>instances for the same ASIC and refuse to try to prevent similar
>situation happening in the new subsystem.
>
>> >> There are currently 3 drivers for dpll I know of. This in ptp_ocp and
>> >> mlx5 there is no concept of sharing pins. You you are talking about a
>> >> single driver.
>> >>
>> >> What I'm trying to say is, looking at the code, the pin sharing,
>> >> references and locking makes things uncomfortably complex. You are so
>> >> far the only driver to need this, do it internally. If in the future
>> >> other driver appears, this code would be eventually pushed into dpll
>> >> core. No impact on UAPI from what I see. Please keep things as simple
>as
>> >> possible.
>> >
>> >But the pin is shared for one driver. Who cares if it's not shared in
>> >another. The user space must be able to reason about the constraints.
>>
>> Sorry, I don't follow :/ Could you please explain what do you mean by
>> this?
>
>We don't wait with adding APIs until there is more than one driver that
>needs them.
>
>> >You are suggesting drivers to magically flip state in core objects
>> >because of some hidden dependencies?!
>>
>> It's not a state flip. It's more like a well propagated event of a state
>> change. The async changes may happen anyway, so the userspace needs
>> to handle them. Why is this different?
>
>What if the user space wants conflicting configurations for the muxes
>behind a shared pin?
>
>The fact that there is a notification does not solve the problem of
>user space not knowing what's going on. Why would the user space play
>guessing games if the driver _knows_ the topology and can easily tell
>it.
>> >> There is a big difference if we model flat list of pins with a set of
>> >> attributes for each, comparing to a tree of pins, some acting as leaf,
>> >> node and root. Do we really need such complexicity? What value does it
>> >> bring to the user to expose this?
>> >
>> >The fact that you can't auto select from devices behind muxes.
>>
>> Why? What's wrong with the mechanism I described in another part of this
>> thread?
>>
>> Extending my example from above
>>
>>    pin 1 source
>>    pin 2 output
>>    pin 3 muxid 100 source
>>    pin 4 muxid 100 source
>>    pin 5 muxid 101 source
>>    pin 6 muxid 101 source
>>    pin 7 output
>>
>> User now can set individial prios for sources:
>>
>> dpll x pin 1 set prio 10
>> etc
>> The result would be:
>>
>>    pin 1 source prio 10
>>    pin 2 output
>>    pin 3 muxid 100 source prio 8
>>    pin 4 muxid 100 source prio 20
>>    pin 5 muxid 101 source prio 50
>>    pin 6 muxid 101 source prio 60
>>    pin 7 output
>>
>> Now when auto is enabled, the pin 3 is selected. Why would user need to
>> manually select between 3 and 4? This is should be abstracted out by the
>> driver.
>>
>> Actually, this is neat as you have one cmd to do selection in manual
>> mode and you have uniform way of configuring/monitoring selection in
>> autosel. Would the muxed pin make this better?
>>
>> For muxed pin being output, only one pin from mux would be set:
>>
>>    pin 1 source
>>    pin 2 output
>>    pin 3 muxid 100 disconnected
>>    pin 4 muxid 100 disconnected
>>    pin 5 muxid 101 output
>>    pin 6 muxid 101 disconnected
>>    pin 7 output
>
>Sorry, can't parse, could you draw the diagram?
>
>To answer the most basic question - my understanding is that for
>prio-based selection there needs to be silicon that can tell if
>there is a valid clock on the line. While mux is just a fancy switch,
>it has no complex logic, just connects wires.
>
>Arkadiusz, is my understanding incorrect? I may have "intuited" this.
>

Yes, exactly.

    +--+       +-----------+
p8---  |   p0---           |
    |  |       |           -----p5
p9---  ----p1---           |
    |  |       |           -----p6
p10--  |   p2---           |
    |  |       |           |
    +--+   p3---           -----p7
               |           |
           p4---           |
               +-----------+
Silicon is configured with priorities for each of the directly connected
source pins (p0-p4, assume p5-p7 are outputs). Thus it can select highest
priority and valid signal of those.
Silicon is responsible to determine if the signal is present and valid for
clock recovery. If so, it can lock to it. If signal is not valid, then silicon
would try to lock to the next highest priority, and so on.
MUX-type pin is here aggregator for additional sources, They cannot be
autoselected by silicon, as they are external for silicon.
If the user want to have dpll "running" on p10, it requires to select p10 and
configure p1 as the highest priority pin.


>IDK if there are any bidirectional pins after a mux, but that'd be
>another problem. Muxes are only simple for inputs.

Same here, haven't heard about such design yet.
IMHO mux-pin is either source that has multiple sources connected or an output
with multiple outputs.
i.e. extending above with:
        +----+ 
        |    ----p11
        |    | 
---p5---|    ----p12
        |    | 
        |    ----p13
        +----+
Where p11-p13 are muxed outputs.
The user is able to change i.e. frequency of p11/p12/p13 for some needs, or
connect/disconnect only one of it. Of course all depends on HW.

Thanks,
Arkadiusz

>
>> >The HW topology is of material importance to user space.
>>
>> Interesting. When I was working on linecards, you said that the user
>> does not care about the inner HW topology. And it makes sense. When
>> things could be abstracted out to make iface clean, I think they should.
>
>I recall only the FW related conversations, but what I think is key
>is whether the information can be acted upon.
>
>> >How many times does Arkadiusz have to explain this :|
>>
>> Pardon my ignorance, I don't see why exactly we need mux hierarchy
>> (trees) exposed to user.

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-08 11:58         ` Jiri Pirko
@ 2022-12-08 23:05           ` Kubalewski, Arkadiusz
  2022-12-09 10:01             ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-08 23:05 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Thursday, December 8, 2022 12:59 PM
>
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Friday, December 2, 2022 5:12 PM
>>>
>>>Fri, Dec 02, 2022 at 12:27:24PM CET, arkadiusz.kubalewski@intel.com
>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Wednesday, November 30, 2022 1:32 PM
>>>>>
>>>>>Tue, Nov 29, 2022 at 10:37:20PM CET, vfedorenko@novek.ru wrote:
>>>>>>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.
>>>>>
>>>>>Overall, I see a lot of issues on multiple levels. I will go over them
>in
>>>>>follow-up emails. So far, after couple of hours looking trought this, I
>>>>>have following general feelings/notes:
>>>>
>>>>Hi Jiri,
>>>>
>>>>As we have been participating in last version, feel obligated to answer
>to
>>>>the concerns.
>>>
>>>Cool.
>>>
>>>
>>>>
>>>>>
>>>>>1) Netlink interface looks much saner than in previous versions. I will
>>>>>   send couple of notes, mainly around events and object mixtures and
>>>>>   couple of bugs/redundancies. But overall, looks fineish.
>>>>>
>>>>>2) I don't like that concept of a shared pin, at all. It makes things
>>>>>   unnecessary complicated. Just have a pin created for dpll instance
>>>>>   and that's it. If another instance has the same pin, it should
>create
>>>>>   it as well. Keeps things separate and easy to model. Let the
>>>>>   hw/fw/driver figure out the implementation oddities.
>>>>>   Why exactly you keep pushing the shared pin idea? Perhaps I'm
>missing
>>>>>   something crucial.
>>>>
>>>>
>>>>If the user request change on pin#0 of dpll#0, the dpll#0 knows about
>the
>>>>change, it reacts accordingly, and notifies the user the something has
>>>changed.
>>>>Which is rather simple.
>>>>
>>>>Now, if the dpll#1 is using the same pin (pin#0 of dpll#0), the
>>>complicated
>>>>part starts. First we have to assume:
>>>>- it was initialized with the same description (as it should, to prevent
>>>>confusing the user)
>>>>- it was initialized with the same order (this is at least nice to have
>>>from
>>>>user POV, as pin indices are auto generated), and also in case of
>multiple
>>>pins
>>>>being shared it would be best for the user to have exactly the same
>number
>>>of
>>>>pins initialized, so they have same indices and initialization order
>>>doesn't
>>>>introduce additional confusion.
>>>>
>>>>Thus, one reason of shared pins was to prevent having this assumptions
>>>ever.
>>>>If the pin is shared, all dplls sharing a pin would have the same
>>>description
>>>>and pin index for a shared pin out of the box.
>>>>
>>>>Pin attribute changes
>>>>The change on dpll#0 pin#0 impacts also dpll#1 pin#0. Notification about
>>>the
>>>>change shall be also requested from the driver that handles dpll#1. In
>>>such
>>>>case the driver has to have some dpll monitoring/notifying mechanics,
>>>which at
>>>>first doesn't look very hard to do, but most likely only if both dplls
>are
>>>>initialized and managed by a single instance of a driver/firmware.
>>>>
>>>>If board has 2 dplls but each one is managed by its own firmware/driver
>>>>instance. User changes frequency of pin#0 signal, the driver of dpll#0
>>>must
>>>>also notify driver of dpll#1 that pin#0 frequency has changed, dpll#1
>>>reacts on
>>>>the change, notifies the user.
>>>
>>>Right.
>>>
>>>
>>>>But this is only doable with assumption, that the board is internally
>>>capable
>>>>of such internal board level communication, which in case of separated
>>>>firmwares handling multiple dplls might not be the case, or it would
>>>require
>>>>to have some other sw component feel that gap.
>>>
>>>Yep, you have the knowledge of sharing inside the driver, so you should
>>>do it there. For multiple instances, use in-driver notifier for example.
>>>
>>
>>This can be done right now, if driver doesn't need built-in pin sharing or
>>doesn't want it, it doesn't have to use it.
>>It was designed this way for a reason, reason of reduced complexity of
>drivers,
>>why should we want to increase complexity of multiple objects instead of
>having
>>it in one place?
>
>Nevermind, don't want to repeat myself. Looks like everyone wants this
>pin sharing infra in the core, let's now focus on designing it properly.
>
>
>>
>>>
>>>>
>>>>For complex boards with multiple dplls/sync channels, multiple ports,
>>>>multiple firmware instances, it seems to be complicated to share a pin
>if
>>>>each driver would have own copy and should notify all the other about
>>>changes.
>>>>
>>>>To summarize, that is certainly true, shared pins idea complicates stuff
>>>>inside of dpll subsystem.
>>>>But at the same time it removes complexity from all the drivers which
>>>would use
>>>
>>>There are currently 3 drivers for dpll I know of. This in ptp_ocp and
>>>mlx5 there is no concept of sharing pins. You you are talking about a
>>>single driver.
>>>
>>>What I'm trying to say is, looking at the code, the pin sharing,
>>>references and locking makes things uncomfortably complex. You are so
>>>far the only driver to need this, do it internally. If in the future
>>>other driver appears, this code would be eventually pushed into dpll
>>>core. No impact on UAPI from what I see. Please keep things as simple as
>>>possible.
>>>
>>
>>The part of interface for drivers is right now 17 functions in total, thus
>I
>>wouldn't call it complicated. References between dpll instances and pins,
>are
>>other part of "increased" complexity, but I wouldn't either call it
>>over-complicated, besides it is part of dpll-core. Any interface shouldn't
>make
>>the user to look into the code. For users only documentation shall be
>important.
>>If kernel module developer is able to read header and understand what he
>needs
>>to do for his hardware, whether he want to use shared pins or not, and
>what
>>impact would it have.
>>
>>Thus real question is, if it is easy enough to use both parts.
>>I would say the kernel module part is easy to understand even by looking
>at
>>the function names and their arguments. Proper documentation would clear
>>anything that is still not clear.
>>
>>Netlink part is also moving in a right direction.
>>
>>>
>>>>it and is easier for the userspace due to common identification of pins.
>>>
>>>By identification, you mean "description" right? I see no problem of 2
>>>instances have the same pin "description"/label.
>>>
>>
>>"description"/label and index as they are the same pin. Index is auto-
>generated
>>and depends on initialization order, this makes unnecessary dependency.
>>
>>>
>>>>This solution scales up without any additional complexity in the driver,
>>>>and without any need for internal per-board communication channels.
>>>>
>>>>Not sure if this is good or bad, but with current version, both
>approaches
>>>are
>>>>possible, so it pretty much depending on the driver to initialize dplls
>>>with
>>>>separated pin objects as you have suggested (and take its complexity
>into
>>>>driver) or just share them.
>>>>
>>>>>
>>>>>3) I don't like the concept of muxed pins and hierarchies of pins. Why
>>>>>   does user care? If pin is muxed, the rest of the pins related to
>this
>>>>>   one should be in state disabled/disconnected. The user only cares
>>>>>   about to see which pins are related to each other. It can be easily
>>>>>   exposed by "muxid" like this:
>>>>>   pin 1
>>>>>   pin 2
>>>>>   pin 3 muxid 100
>>>>>   pin 4 muxid 100
>>>>>   pin 5 muxid 101
>>>>>   pin 6 muxid 101
>>>>>   In this example pins 3,4 and 5,6 are muxed, therefore the user knows
>>>>>   if he connects one, the other one gets disconnected (or will have to
>>>>>   disconnect the first one explicitly first).
>>>>>
>>>>
>>>>Currently DPLLA_PIN_PARENT_IDX is doing the same thing as you described,
>>>it
>>>>groups MUXed pins, the parent pin index here was most straightforward to
>>>me,
>>>
>>>There is a big difference if we model flat list of pins with a set of
>>>attributes for each, comparing to a tree of pins, some acting as leaf,
>>>node and root. Do we really need such complexicity? What value does it
>>>bring to the user to expose this?
>>>
>>
>>If the one doesn't need to use muxed pins, everything is simple, as you
>have
>>described. In fact in the example you have given, only benefit I can see
>from
>>muxid is that user knows which pins would get disconnected when one of the
>mux
>>group pins is selected. But do user really needs that knowledge? If one
>pin
>>was selected why should he look on the other pins?
>
>Right. That would be probably valuable only for output pins.
>
>
>>
>>Anyway I see your point, but this is not only about identifying a muxed
>pins,
>>it is more about identifying connections between them because of hardware
>>capabilities they can bring, and utilization of such capabilities.
>>Please take a look on a comment below.
>>
>>>
>>>>as in case of DPLL_MODE_AUTOMATIC, where dpll auto-selects highest
>>>priority
>>>>available signal. The priority can be only assigned to the pins directly
>>>>connected to the dpll. The rest of pins (which would have present
>>>>attribute DPLLA_PIN_PARENT_IDX) are the ones that require manual
>selection
>>>>even if DPLL_MODE_AUTOMATIC is enabled.
>>>>
>>>>Enabling a particular pin and sub-pin in DPLL_MODE_AUTOMATIC requires
>from
>>>user
>>>>to select proper priority on on a dpll-level MUX-pin and manually select
>>>one of
>>>>the sub-pins.
>>>>On the other hand for DPLL_MODE_FORCED, this might be also beneficial,
>as
>>>the
>>>>user could select a directly connected pin and muxed pin with two
>>>separated
>>>>commands, which could be handled in separated driver instances (if HW
>>>design
>>>>requires such approach) or either it can be handled just by one select
>>>call
>>>>for the pin connected directly and handled entirely in the one driver
>>>instance.
>>>
>>>Talk netlink please. What are the actual commands with cmds used to
>>>select the source and select a mux pin? You are talking about 2 types of
>>>selections:
>>>1) Source select
>>>   - this is as you described either auto/forces (manual) mode,
>>>   according to some prio, dpll select the best source
>>>2) Pin select in a mux
>>>   - here the pin could be source or output
>>>
>>>But again, from the user perspective, why does he have to distinguish
>>>these? Extending my example:
>>>
>>>   pin 1 source
>>>   pin 2 output
>>>   pin 3 muxid 100 source
>>>   pin 4 muxid 100 source
>>>   pin 5 muxid 101 source
>>>   pin 6 muxid 101 source
>>>   pin 7 output
>>>
>>>User now can set individial prios for sources:
>>>
>>>dpll x pin 1 set prio 10
>>>etc
>>>The result would be:
>>>
>>>   pin 1 source prio 10
>>>   pin 2 output
>>>   pin 3 muxid 100 source prio 8
>>>   pin 4 muxid 100 source prio 20
>>>   pin 5 muxid 101 source prio 50
>>>   pin 6 muxid 101 source prio 60
>>>   pin 7 output
>>>
>>>Now when auto is enabled, the pin 3 is selected. Why would user need to
>>>manually select between 3 and 4? This is should be abstracted out by the
>>>driver.
>>
>>Yes, this could be abstracted in the driver, but we are talking here about
>>different things. We need automatic hardware-supported selection of a pin,
>>not automatic software-supported selection, where driver (part of software
>>stack) is doing autoselection.
>>
>>If dpll is capable of hw-autoselection, the priorities are configured in
>the
>>hardware, not in the driver. This interface is a proxy between user and
>dpll.
>
>Okay, this is very nice example, where the confusion appeared. Looking
>into your code, function dpll_pin_attr_from_nlattr(), it clearly allows
>userspace to set the prio over DPLLA_PIN_PRIO. Yet you claim the prio is
>configured in the hardware.
>

Yes, userspace is requesting this configuration in hardware. It doesn't
change anything, the hw-supported auto-selection works only on the source pins
connected directly. Mux-type pin can have priority assigned by the user and
still need to select one of multiple pins associated with it. 

>
>>The one could use it as you have described by implementing auto-selection
>>in SW, but those are two different modes, actually we could introduce
>modes
>>like: DPLL_MODE_HW_AUTOMATIC and DPLL_MODE_SW_AUTOMATIC, to clear things
>up.
>>Although, IMHO, DPLL_MODE_SW_AUTOMATIC wouldn't really differ from current
>>behavior of DPLL_MODE_FORCED, where the userspace is explicitly selecting
>a
>>source, and the only difference would be that selection comes from driver
>>instead of userspace tool. Thus in the end it would just increase
>complexity
>>of a driver (instead of bringing any benefits).
>
>Yeah, does not make sense to have DPLL_MODE_SW_AUTOMATIC as you describe
>it. I agree.
>
>>
>>>
>>>Only issues I see when pins are output. User would have to somehow
>>>select one of the pins in the mux (perhaps another dpll cmd). But the
>>>mux pin instance does not help with anything there...
>>>
>>>
>>>
>>>>
>>>>>4) I don't like the "attr" indirection. It makes things very tangled.
>It
>>>>>   comes from the concepts of classes and objects and takes it to
>>>>>   extreme. Not really something we are commonly used to in kernel.
>>>>>   Also, it brings no value from what I can see, only makes things very
>>>>>   hard to read and follow.
>>>>>
>>>>
>>>>Yet again, true, I haven't find anything similar in the kernel, it was
>>>more
>>>>like a try to find out a way to have a single structure with all the
>stuff
>>>that
>>>>is passed between netlink/core/driver parts. Came up with this, and to
>be
>>>>honest it suits pretty well, those are well defined containers. They
>store
>>>>attributes that either user or driver have set, with ability to obtain a
>>>valid
>>>>value only if it was set. Thus whoever reads a struct, knows which of
>>>those
>>>>attributes were actually set.
>>>
>>>Sorry for being blunt here, but when I saw the code I remembered my days
>>>as a student where they forced us to do similar things Java :)
>>>There you tend to make things contained, everything is a class, getters,
>>>setters and whatnot. In kernel, this is overkill.
>>>
>>>I'm not saying it's functionally wrong. What I say is that it is
>>>completely unnecessary. I see no advantage, by having this indirection.
>>>I see only disadvantages. It makes code harder to read and follow.
>>>What I suggest, again, is to make things nice and simple. Set of ops
>>>that the driver implements for dpll commands or parts of commands,
>>>as we are used to in the rest of the kernel.
>>>
>>
>>No problem, I think we will get rid of it, see comment below.
>>
>>>
>>>>As you said, seems a bit revolutionary, but IMHO it simplifies stuff,
>and
>>>>basically it is value and validity bit, which I believe is rather common
>>>in the
>>>>kernel, this differs only with the fact it is encapsulated. No direct
>>>access to
>>>>the fields of structure is available for the users.
>>>
>>>I don't see any reason for any validity bits whan you just do it using
>>>driver-implemented ops.
>>>
>>>
>>>>Most probably there are some things that could be improved with it, but
>in
>>>>general it is very easy to use and understand how it works.
>>>>What could be improved:
>>>>- naming scheme as function names are a bit long right now, although
>>>mostly
>>>>still fits the line-char limits, thus not that problematic
>>>>- bit mask values are capable of storing 32 bits and bit(0) is always
>used
>>>as
>>>>unspec, which ends up with 31 values available for the enums so if by
>any
>>>>chance one of the attribute enums would go over 32 it could be an issue.
>>>>
>>>>It is especially useful for multiple values passed with the same netlink
>>>>attribute id. I.e. please take a look at
>>>dpll_msg_add_pin_types_supported(..)
>>>>function.
>>>>
>>>>>   Please keep things direct and simple:
>>>>>   * If some option could be changed for a pin or dpll, just have an
>>>>>     op that is directly called from netlink handler to change it.
>>>>>     There should be clear set of ops for configuration of pin and
>>>>>     dpll object. This "attr" indirection make this totally invisible.
>>>>
>>>>In last review you have asked to have rather only set and get ops
>defined
>>>>with a single attribute struct. This is exactly that, altough
>>>encapsulated.
>>>
>>>For objects, yes. Pass a struct you directly read/write if the amount of
>>>function args would be bigger then say 4. The whole encapsulation is not
>>>good for anything.
>>
>>If there is one set/get for whole object, then a writer of a struct
>(netlink
>>or driver) has to have possibility to indicate which parts of the struct
>were
>>actually set, so a reader can do things that were requested.
>>
>>But I agree this is probably not any better to the "ops per attribute"
>approach
>>we have had before, thus I think we shall get back to this approach and
>remove
>>dpll_attr/dpll_pin_attr entirely.
>>
>>>
>>>
>>>>
>>>>>   * If some attribute is const during dpll or pin lifetime, have it
>>>>>     passed during dpll or pin creation.
>>>>>
>>>>>
>>>>
>>>>Only driver knows which attributes are const and which are not, this
>shall
>>>
>>>Nonono. This is semantics defined by the subsystem. The pin
>>>label/description for example. That is const, cannot be set by the user.
>>
>>True, it is const and it is passed on pin init.
>>
>>>The type of the pin (synce/gnss/ext) is const, cannot be set by the user.
>>
>>It can, as input/source can change, thus the same way, type of pin could
>be
>>managed by some hardware fuses/switches/etc.
>
>Could you please describe this a bit more? For example how the user can
>change synce pin to ext pin? I still fail to understand how such thing
>is possible. How the HW could be rewired on user request?
>Perhaps we understand differently the "pin" object. I understand is as
>something out-facing. Not an actual pin of a chip.
>

Sorry, but I don't have any real-life example of it. I don't think someone
would design this as a choice between ext and synce, but hardware design may
allow user such thing, more likely it would be a pin where the user selects if
it is ext or gnss.

I agree, you are right that this can be modeled with the mux-type pin
instead of changing the pin-type.

Although there is small benefit of reduced complexity of a driver, by allowing
change of pin-type instead of having mux type pin and additional pins
associated with it, I won't insist on any of those solutions.
If there is no other objections we can change dpll_pin_type attribute
to become a const pin init parameter.

>
>>
>>>This you have to clearly specify when you define driver API.
>>>This const attrs should be passed during pin creation/registration.
>>>
>>>Talking about dpll instance itself, the clock_id, clock_quality, these
>>>should be also const attrs.
>>>
>>
>>Actually, clock_quality can also vary on runtime (i.e. ext/synce). We
>cannot
>>determine what Quality Level signal user has connected to the SMA or was
>>received from the network. Only gnss/oscilattor could have const depending
>>on used HW. But generally it shall not be const.
>
>Sec. I'm talkign about the actual dpll quality, means the internal
>clock. How it can vary?

Yes, the DPLL has some holdover capacity, thus can translate this into QL and
it shall not ever change. Sure, we could add this.

I was thinking about a source Quality Level. If that would be available here,
the ptp-profiles implementation would be simpler, as ptp daemon could read it
and embed that information in its frames.
Although, this would have to be configurable from user space, at least for EXT
and SYNCE pin types.

Thanks,
Arkadiusz

>
>
>>
>>Thanks,
>>Arkadiusz
>>
>>>
>>>
>>>>be also part of driver implementation.
>>>>As I understand all the fields present in (dpll/dpll_pin)_attr, used in
>>>get/set
>>>>ops, could be altered in run-time depending on HW design.
>>>>
>>>>Thanks,
>>>>Arkadiusz
>>>>
>>>>>
>>>>>>
>>>>>>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 (1):
>>>>>>  dpll: add dpll_attr/dpll_pin_attr helper classes
>>>>>>
>>>>>>Vadim Fedorenko (3):
>>>>>>  dpll: Add DPLL framework base functions
>>>>>>  dpll: documentation on DPLL subsystem interface
>>>>>>  ptp_ocp: implement DPLL ops
>>>>>>
>>>>>> Documentation/networking/dpll.rst  | 271 ++++++++
>>>>>> Documentation/networking/index.rst |   1 +
>>>>>> MAINTAINERS                        |   8 +
>>>>>> drivers/Kconfig                    |   2 +
>>>>>> drivers/Makefile                   |   1 +
>>>>>> drivers/dpll/Kconfig               |   7 +
>>>>>> drivers/dpll/Makefile              |  11 +
>>>>>> drivers/dpll/dpll_attr.c           | 278 +++++++++
>>>>>> drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
>>>>>> drivers/dpll/dpll_core.h           | 176 ++++++
>>>>>> drivers/dpll/dpll_netlink.c        | 963
>+++++++++++++++++++++++++++++
>>>>>> drivers/dpll/dpll_netlink.h        |  24 +
>>>>>> drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
>>>>>> drivers/ptp/Kconfig                |   1 +
>>>>>> drivers/ptp/ptp_ocp.c              | 123 ++--
>>>>>> include/linux/dpll.h               | 261 ++++++++
>>>>>> include/linux/dpll_attr.h          | 433 +++++++++++++
>>>>>> include/uapi/linux/dpll.h          | 263 ++++++++
>>>>>> 18 files changed, 4002 insertions(+), 37 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_attr.c 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/dpll/dpll_pin_attr.c create mode 100644 include/linux/dpll.h
>>>>>> create mode 100644 include/linux/dpll_attr.h create mode 100644
>>>>>> include/uapi/linux/dpll.h
>>>>>>
>>>>>>--
>>>>>>2.27.0
>>>>>>

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-08 11:22                 ` Jiri Pirko
@ 2022-12-09  0:36                   ` Jakub Kicinski
  2022-12-09  9:32                     ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-09  0:36 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-clk

On Thu, 8 Dec 2022 12:22:09 +0100 Jiri Pirko wrote:
> >To what practical benefit? Where do we draw the line? Do you want
> >PTP clocks to also be auxdevs? DPLL lives in netdev, we don't have
> >to complicate things. auxdev is a Conway's law solution.  
> 
> Auxdev infra is quite simple to implement, I'm not sure what do you mean
> by complicating thing here.

You didn't answer my question - what's the benefit?
We're not faced with A or B choice. We have a A or nothing choice.
Doing nothing is easy.

> >mlx5 already looks like sausage meat, it's already minced so you can
> >fit it there quite easily, but don't force it on non-enterprise devices.  
> 
> Not forcing, just suggesting. It's a low-hanging fruit, why not reach
> it?

What is the fruit?

> >There is non 1:1 relationship with a bus device and subsystem in Linux,
> >LMK when you convinced Greg otherwise.  
> 
> Sure there is not. But maybe that is due to the simple fact that auxdev
> was introduces, what, 2 years back? My point is, we are introducing new
> subsystem, wouldn't it be nice to start it clean?

Still not getting what you think is clean.. Making all driver-facing
objects in the kernel be a fake bus-device?!

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-08 11:28             ` Jiri Pirko
@ 2022-12-09  0:39               ` Jakub Kicinski
  2022-12-09  0:56                 ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-09  0:39 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev.dump, 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

On Thu, 8 Dec 2022 12:28:51 +0100 Jiri Pirko wrote:
> >I think we discussed using serial numbers.  
> 
> Can you remind it? Do you mean serial number of pin?

Serial number of the ASIC, board or device.
Something will have a serno, append to that your pin id of choice -
et voila!

> >Are you saying within the driver it's somehow easier? The driver state
> >is mostly per bus device, so I don't see how.  
> 
> You can have some shared data for multiple instances in the driver code,
> why not?

The question is whether it's easier.
Easier to ensure quality of n implementations in random drivers. 
Or one implementation in the core, with a lot of clever people
paying attention and reviewing the code.

> >> There are many problems with that approach, and the submitted patch is not
> >> explaining any of them. E.g. it contains the dpll_muxed_pin_register but no
> >> free 
> >> counterpart + no flows.  
> >
> >SMOC.  
> 
> Care to spell this out. I guess you didn't mean "South Middlesex
> Opportunity Council" :D

Simple matter of coding.

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-07 23:21           ` Jakub Kicinski
  2022-12-08 11:28             ` Jiri Pirko
  2022-12-08 18:08             ` Maciek Machnikowski
@ 2022-12-09  0:46             ` Kubalewski, Arkadiusz
  2 siblings, 0 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-09  0:46 UTC (permalink / raw)
  To: Jakub Kicinski, netdev.dump
  Cc: 'Jiri Pirko', 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Thursday, December 8, 2022 12:22 AM
>
>On Wed, 7 Dec 2022 15:09:03 +0100 netdev.dump@gmail.com wrote:
>> > -----Original Message-----
>> > From: Jakub Kicinski <kuba@kernel.org>
>> > Sent: Wednesday, December 7, 2022 3:48 AM
>> > Subject: Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration
>API
>> >
>> > On Fri, 2 Dec 2022 17:12:06 +0100 Jiri Pirko wrote:
>>  [...]
>> capable
>>  [...]
>> require
>>  [...]
>
>Please fix line wrapping in your email client.
>And add a name to your account configuration :/
>
>> > > Yep, you have the knowledge of sharing inside the driver, so you
>should
>> > > do it there. For multiple instances, use in-driver notifier for
>example.
>> >
>> > No, complexity in the drivers is not a good idea. The core should cover
>> > the complexity and let the drivers be simple.
>>
>> But how does Driver A know where to connect its pin to? It makes sense to
>> share
>
>I think we discussed using serial numbers.

Right now, driver can find dpll with:
struct dpll_device *dpll_device_get_by_cookie(u8 cookie[DPLL_COOKIE_LEN],
                                              enum dpll_type type, u8 idx);
Where arguments would be the same as given when first instance have allocated
dpll with:
struct dpll_device
*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
                   const u8 cookie[DPLL_COOKIE_LEN], u8 dev_driver_idx,
                   void *priv, struct device *parent);

Which means all driver instances must know those values if they need to share
dpll or pins.

>
>> pins between the DPLLs exposed by a single driver, but not really outside
>of
>> it.
>> And that can be done simply by putting the pin ptr from the DPLLA into
>the
>> pin
>> list of DPLLB.
>
>Are you saying within the driver it's somehow easier? The driver state
>is mostly per bus device, so I don't see how.
>
>> If we want the kitchen-and-sink solution, we need to think about corner
>> cases.
>> Which pin should the API give to the userspace app - original, or
>> muxed/parent?
>
>IDK if I parse but I think both. If selected pin is not directly
>attached the core should configure muxes.

If there is real need for muxed pin (hardware with support for priority based
Auto-selection or some other hardware constraints), then both.
As priority is set on mux-type/parent pin, but selection of muxed pin would be
done manually with DPLL_CMD_DEVICE_SET and given DPLLA_SOURCE_PIN_IDX.
If the hardware doesn't support priority based auto-selection, then it might
be better to just add new pin into existing dpll without any mux-type pins,
this way your driver would be simpler. But also possible to follow the same
approach, add mux-type parent on one instance and register new pins from
different instances with that parent, both are propagated to userspace app.

>
>> How would a teardown look like - if Driver A registered DPLLA with Pin1
>and
>> Driver B added the muxed pin then how should Driver A properly
>> release its pins? Should it just send a message to driver B and trust
>that
>> it
>> will receive it in time before we tear everything apart?
>
>Trivial.

With current version...
Driver A creates dpll (as it was initialized first).
Driver B:
- allocates new pin
- finds existing parent pin (find dpll (dpll_device_get_by_cookie), find pin
(dpll_pin_get_by_description).
- registers new pin with parent pin found.

For dealloc Driver B shall deregister and free it's pin:
dpll_pin_deregister(struct dpll_device *dpll, struct dpll_pin *pin);
dpll_pin_free(struct dpll_pin *pin);
This shall be done before Driver A deregisters dpll.
As long as there is a reference to the Driver B registered pin in dpll created
by Driver A, the dpll won't be deregistered.

>
>> There are many problems with that approach, and the submitted patch is
>not
>> explaining any of them. E.g. it contains the dpll_muxed_pin_register but
>no
>> free
>> counterpart + no flows.
>
>SMOC.
>

_register counterpart is _deregister
_alloc counterpart is _free
Whichever pin-register function the one would use, it shall use
dpll_pin_deregister(..) when releasing resource.

>> If we want to get shared pins, we need a good example of how this
>mechanism
>> can be used.
>
>Agreed.

Shall be provided in next version of patch series.

>
>> > > There are currently 3 drivers for dpll I know of. This in ptp_ocp and
>> > > mlx5 there is no concept of sharing pins. You you are talking about a
>> > > single driver.
>> > >
>> > > What I'm trying to say is, looking at the code, the pin sharing,
>> > > references and locking makes things uncomfortably complex. You are so
>> > > far the only driver to need this, do it internally. If in the future
>> > > other driver appears, this code would be eventually pushed into dpll
>> > > core. No impact on UAPI from what I see. Please keep things as simple
>as
>> > > possible.
>> >
>> > But the pin is shared for one driver. Who cares if it's not shared in
>> > another. The user space must be able to reason about the constraints.
>> >
>> > You are suggesting drivers to magically flip state in core objects
>> > because of some hidden dependencies?!
>>
>> If we want to go outside the device, we'd need some universal language
>> to describe external connections - such as the devicetree. I don't see
>how
>> we can reliably implement inter-driver dependency otherwise.
>
>There's plenty examples in the tree. If we can't use serial number
>directly we can compare the driver pointer + whatever you'd compare
>in the driver internal solution.
>
>> I think this would be better served in the userspace with a board-
>specific
>> config file. Especially since the pins can be externally connected
>anyway.
>
>Opinions vary, progress is not being made.

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-08 12:02             ` Jiri Pirko
@ 2022-12-09  0:54               ` Jakub Kicinski
  0 siblings, 0 replies; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-09  0:54 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, linux-clk

On Thu, 8 Dec 2022 13:02:09 +0100 Jiri Pirko wrote:
> Wed, Dec 07, 2022 at 06:19:46PM CET, kuba@kernel.org wrote:
> >On Wed, 7 Dec 2022 15:51:42 +0100 Jiri Pirko wrote:  
> >> Really, even in case only one driver actually consumes the complexicity?
> >> I understand having a "libs" to do common functionality for drivers,
> >> even in case there is one. But this case, I don't see any benefit.  
> >
> >In the same email thread you admit that mlx5 has multiple devlink
> >instances for the same ASIC and refuse to try to prevent similar
> >situation happening in the new subsystem.  
> 
> I don't understand your point. In CX there is 1 clock for 2 pci PFs. I
> plan to have 1 dpll instance for the clock shared.
> 
> But how is what you write relevant to the discussion? We are talking
> about:
> a) 1 pin in 2 dpll instances
> what I undestand you say here is to prevent:
> b) 2 dpll instances for 1 clock
> apples and oranges. Am I missing something?
> 
> I'm totally against b) but that is not what we discuss here, correct?

Correct, neither (a), nor (b), and as much code for "find other PFs
referring to this device on the board" moved into the core as possible.

AFAIU (and if I'm reading the docs Maciek linked) in case of Intel
there are actually multiple DPLL instances in a single package / Si die.
I presume we want those to be a separate DPLL instance each?
But being part of a single die they share pins.. 

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-09  0:39               ` Jakub Kicinski
@ 2022-12-09  0:56                 ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-09  0:56 UTC (permalink / raw)
  To: Jakub Kicinski, Jiri Pirko
  Cc: netdev.dump, 'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Jakub Kicinski <kuba@kernel.org>
>Sent: Friday, December 9, 2022 1:40 AM
>On Thu, 8 Dec 2022 12:28:51 +0100 Jiri Pirko wrote:
>> >I think we discussed using serial numbers.
>>
>> Can you remind it? Do you mean serial number of pin?
>
>Serial number of the ASIC, board or device.
>Something will have a serno, append to that your pin id of choice - et
>voila!

Right now, driver can find dpll with:
struct dpll_device *dpll_device_get_by_cookie(u8 cookie[DPLL_COOKIE_LEN],
                                              enum dpll_type type, u8 idx); 

Where arguments would be the same as given when first instance have allocated
dpll with:
struct dpll_device
*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
                   const u8 cookie[DPLL_COOKIE_LEN], u8 dev_driver_idx,
                   void *priv, struct device *parent);

Which means all driver instances must know those values if they need to share
dpll or pins.

Thanks,
Arkadiusz

>
>> >Are you saying within the driver it's somehow easier? The driver
>> >state is mostly per bus device, so I don't see how.
>>
>> You can have some shared data for multiple instances in the driver
>> code, why not?
>
>The question is whether it's easier.
>Easier to ensure quality of n implementations in random drivers.
>Or one implementation in the core, with a lot of clever people paying
>attention and reviewing the code.
>
>> >> There are many problems with that approach, and the submitted patch
>> >> is not explaining any of them. E.g. it contains the
>> >> dpll_muxed_pin_register but no free counterpart + no flows.
>> >
>> >SMOC.
>>
>> Care to spell this out. I guess you didn't mean "South Middlesex
>> Opportunity Council" :D
>
>Simple matter of coding.

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-08 17:05                                 ` Jakub Kicinski
@ 2022-12-09  9:29                                   ` Jiri Pirko
  2022-12-09 16:19                                     ` Jakub Kicinski
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-09  9:29 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

Thu, Dec 08, 2022 at 06:05:17PM CET, kuba@kernel.org wrote:
>On Thu, 8 Dec 2022 17:33:28 +0100 Jiri Pirko wrote:
>> For any synce pin manipulation over dpll netlink, we can use the netns
>> check of the linked netdev. This is the netns aware leg of the dpll,
>> it should be checked for.
>
>The OCP card is an atomic clock, it does not have any networking.

Sure, so why it has to be netns aware if it has nothing to do with
networking?


>
>> I can't imagine practically havind the whole dpll instance netns aware.
>> Omitting the fact that it really has no meaning for non-synce pins, what
>> would be the behaviour when for example pin 1 is in netns a, pin 2 in
>> netns b and dpll itself in netns c?
>
>To be clear I don't think it's a bad idea in general, I've done 
>the same thing for my WIP PSP patches. But we already have one
>device without netdevs, hence I thought maybe devlink. So maybe
>we do the same thing with devlink? I mean - allow multiple devlink
>instances to be linked and require caps on any of them?

I read this 5 times, I'm lost, don't understand what you mean :/

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-09  0:36                   ` Jakub Kicinski
@ 2022-12-09  9:32                     ` Jiri Pirko
  0 siblings, 0 replies; 87+ messages in thread
From: Jiri Pirko @ 2022-12-09  9:32 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-clk

Fri, Dec 09, 2022 at 01:36:34AM CET, kuba@kernel.org wrote:
>On Thu, 8 Dec 2022 12:22:09 +0100 Jiri Pirko wrote:
>> >To what practical benefit? Where do we draw the line? Do you want
>> >PTP clocks to also be auxdevs? DPLL lives in netdev, we don't have
>> >to complicate things. auxdev is a Conway's law solution.  
>> 
>> Auxdev infra is quite simple to implement, I'm not sure what do you mean
>> by complicating thing here.
>
>You didn't answer my question - what's the benefit?
>We're not faced with A or B choice. We have a A or nothing choice.
>Doing nothing is easy.
>
>> >mlx5 already looks like sausage meat, it's already minced so you can
>> >fit it there quite easily, but don't force it on non-enterprise devices.  
>> 
>> Not forcing, just suggesting. It's a low-hanging fruit, why not reach
>> it?
>
>What is the fruit?
>
>> >There is non 1:1 relationship with a bus device and subsystem in Linux,
>> >LMK when you convinced Greg otherwise.  
>> 
>> Sure there is not. But maybe that is due to the simple fact that auxdev
>> was introduces, what, 2 years back? My point is, we are introducing new
>> subsystem, wouldn't it be nice to start it clean?
>
>Still not getting what you think is clean.. Making all driver-facing
>objects in the kernel be a fake bus-device?!

Nevermind.

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-08 23:05           ` Kubalewski, Arkadiusz
@ 2022-12-09 10:01             ` Jiri Pirko
  0 siblings, 0 replies; 87+ messages in thread
From: Jiri Pirko @ 2022-12-09 10:01 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Vadim Fedorenko, Jakub Kicinski, Jonathan Lemon, Paolo Abeni,
	netdev, linux-arm-kernel, linux-clk

Fri, Dec 09, 2022 at 12:05:43AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Thursday, December 8, 2022 12:59 PM
>>
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Friday, December 2, 2022 5:12 PM
>>>>
>>>>Fri, Dec 02, 2022 at 12:27:24PM CET, arkadiusz.kubalewski@intel.com
>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Wednesday, November 30, 2022 1:32 PM
>>>>>>
>>>>>>Tue, Nov 29, 2022 at 10:37:20PM CET, vfedorenko@novek.ru wrote:

[...]

>>>>This you have to clearly specify when you define driver API.
>>>>This const attrs should be passed during pin creation/registration.
>>>>
>>>>Talking about dpll instance itself, the clock_id, clock_quality, these
>>>>should be also const attrs.
>>>>
>>>
>>>Actually, clock_quality can also vary on runtime (i.e. ext/synce). We
>>cannot
>>>determine what Quality Level signal user has connected to the SMA or was
>>>received from the network. Only gnss/oscilattor could have const depending
>>>on used HW. But generally it shall not be const.
>>
>>Sec. I'm talkign about the actual dpll quality, means the internal
>>clock. How it can vary?
>
>Yes, the DPLL has some holdover capacity, thus can translate this into QL and
>it shall not ever change. Sure, we could add this.
>
>I was thinking about a source Quality Level. If that would be available here,
>the ptp-profiles implementation would be simpler, as ptp daemon could read it
>and embed that information in its frames.
>Although, this would have to be configurable from user space, at least for EXT
>and SYNCE pin types.

The kernel would serve as a holder or info shared from one daemon to
another one. That does not sound correct. PTP should ask SyncE deamon
directly, I believe.


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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-08 18:08             ` Maciek Machnikowski
@ 2022-12-09 11:07               ` Jiri Pirko
  2022-12-09 14:09                 ` Maciek Machnikowski
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-09 11:07 UTC (permalink / raw)
  To: Maciek Machnikowski
  Cc: Jakub Kicinski, 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

Thu, Dec 08, 2022 at 07:08:04PM CET, maciek@machnikowski.net wrote:
>On 12/8/2022 12:21 AM, Jakub Kicinski wrote:
>> On Wed, 7 Dec 2022 15:09:03 +0100 netdev.dump@gmail.com wrote:
>>>> -----Original Message-----
>>>> From: Jakub Kicinski <kuba@kernel.org>
>>> pins between the DPLLs exposed by a single driver, but not really outside of
>>> it.
>>> And that can be done simply by putting the pin ptr from the DPLLA into the
>>> pin
>>> list of DPLLB.
>> 
>> Are you saying within the driver it's somehow easier? The driver state
>> is mostly per bus device, so I don't see how.
>> 
>>> If we want the kitchen-and-sink solution, we need to think about corner
>>> cases.
>>> Which pin should the API give to the userspace app - original, or
>>> muxed/parent?
>> 
>> IDK if I parse but I think both. If selected pin is not directly
>> attached the core should configure muxes.
>> 
>>> How would a teardown look like - if Driver A registered DPLLA with Pin1 and
>>> Driver B added the muxed pin then how should Driver A properly
>>> release its pins? Should it just send a message to driver B and trust that
>>> it
>>> will receive it in time before we tear everything apart?
>> 
>> Trivial.
>> 
>>> There are many problems with that approach, and the submitted patch is not
>>> explaining any of them. E.g. it contains the dpll_muxed_pin_register but no
>>> free 
>>> counterpart + no flows.
>> 
>> SMOC.
>> 
>>> If we want to get shared pins, we need a good example of how this mechanism
>>> can be used.
>> 
>> Agreed.
>
>My main complaint about the current pins implementation is that they put
>everything in a single bag. In a netdev world - it would be like we put
>TX queues and RX queues together, named them "Queues", expose a list to
>the userspace and let the user figure out which ones which by reading a
>"TX" flag.
>
>All DPLLs I know have a Sources block, DPLLs and Output blocks. See:
>
>https://www.renesas.com/us/en/products/clocks-timing/jitter-attenuators-frequency-translation/8a34044-multichannel-dpll-dco-four-eight-channels#overview
>
>https://ww1.microchip.com/downloads/aemDocuments/documents/TIM/ProductDocuments/ProductBrief/ZL3063x-System-Synchronizers-with-up-to-5-Channels-10-Inputs-20-Outputs-Product-Brief-DS20006634.pdf
>
>https://www.sitime.com/support/resource-library/product-briefs/cascade-sit9514x-clock-system-chip-family
>
>https://www.ti.com/lit/ds/symlink/lmk5b33414.pdf?ts=1670516132647&ref_url=https%253A%252F%252Fwww.ti.com%252Fclocks-timing%252Fjitter-cleaners-synchronizers%252Fproducts.html
>
>If we model everything as "pins" we won't be able to correctly extend
>the API to add new features.
>
>Sources can configure the expected frequency, input signal monitoring
>(on multiple layers), expected signal levels, input termination and so
>on. Outputs will need the enable flag, signal format, frequency, phase
>offset etc. Multiple DPLLs can reuse a single source inside the same
>package simultaneously.


Looking at the documentation of the chips, they all have mupltiple DPLLs
on a die. Arkadiusz, in your proposed implementation, do you model each
DPLL separatelly? If yes, then I understand the urgency of need of a
shared pin. So all DPLLs sharing the pin are part of the same chip?

Question: can we have an entity, that would be 1:1 mapped to the actual
device/chip here? Let's call is "a synchronizer". It would contain
multiple DPLLs, user-facing-sources(input_connector),
user-facing-outputs(output_connector), i/o pins.

An example:
                               SYNCHRONIZER

                              ┌───────────────────────────────────────┐
                              │                                       │
                              │                                       │
  SyncE in connector          │              ┌─────────┐              │     SyncE out connector
                ┌───┐         │in pin 1      │DPLL_1   │     out pin 1│    ┌───┐
                │   ├─────────┼──────────────┤         ├──────────────┼────┤   │
                │   │         │              │         │              │    │   │
                └───┘         │              │         │              │    └───┘
                              │              │         │              │
                              │           ┌──┤         │              │
   GNSS in connector          │           │  └─────────┘              │
                ┌───┐         │in pin 2   │                  out pin 2│     EXT SMA connector
                │   ├─────────┼───────────┘                           │    ┌───┐
                │   │         │                           ┌───────────┼────┤   │
                └───┘         │                           │           │    │   │
                              │                           │           │    └───┘
                              │                           │           │
   EXT SMA connector          │                           │           │
                ┌───┐   mux   │in pin 3      ┌─────────┐  │           │
                │   ├────┬────┼───────────┐  │         │  │           │
                │   │    │    │           │  │DPLL_2   │  │           │
                └───┘    │    │           │  │         │  │           │
                         │    │           └──┤         ├──┘           │
                         │    │              │         │              │
   EXT SMA connector     │    │              │         │              │
                ┌───┐    │    │              │         │              │
                │   ├────┘    │              └─────────┘              │
                │   │         │                                       │
                └───┘         └───────────────────────────────────────┘

Do I get that remotelly correct?

synch
synchronizer_register(synch)
   dpll_1
   synchronizer_dpll_register(synch, dpll_1)
   dpll_2
   synchronizer_dpll_register(synch, dpll_2)
   source_pin_1
   synchronizer_pin_register(synch, source_pin_1)
   output_pin_1
   synchronizer_pin_register(synch, output_pin_1)
   output_pin_2
   synchronizer_pin_register(synch, output_pin_2)

synch_board
   synchronizer_board_register(synch_board)
   synch
   synchronizer_board_sync_register(synch_board, synch)
   source_connector_1
   synchronizer_board_connector_register(synch_board, source_connector_1, source_pin_1)
   output_connector_1
   synchronizer_board_connector_register(synch_board, output_connector_1, output_pin_1)
   output_connector_2
   synchronizer_board_connector_register(synch_board, output_connector_2, output_pin_2)


Thinking about it a bit more, this should be probably good to describe
by device tree. The synchronizer itself dplls and pins it contains
have constanc geometry, according to the synchronizer device type.

The Connector-pin linkages may vary according to the board.

So to divide it, there should be one synchronizer driver. Then probably
some other one to connect/select/mux the connectors to the synchronizer.

Makes sense?

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-09 11:07               ` Jiri Pirko
@ 2022-12-09 14:09                 ` Maciek Machnikowski
  2022-12-09 16:31                   ` Jakub Kicinski
  0 siblings, 1 reply; 87+ messages in thread
From: Maciek Machnikowski @ 2022-12-09 14:09 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk



On 12/9/2022 12:07 PM, Jiri Pirko wrote:
> Thu, Dec 08, 2022 at 07:08:04PM CET, maciek@machnikowski.net wrote:
>> On 12/8/2022 12:21 AM, Jakub Kicinski wrote:
>> My main complaint about the current pins implementation is that they put
>> everything in a single bag. In a netdev world - it would be like we put
>> TX queues and RX queues together, named them "Queues", expose a list to
>> the userspace and let the user figure out which ones which by reading a
>> "TX" flag.
>>
>> All DPLLs I know have a Sources block, DPLLs and Output blocks. See:
>>
>> https://www.renesas.com/us/en/products/clocks-timing/jitter-attenuators-frequency-translation/8a34044-multichannel-dpll-dco-four-eight-channels#overview
>>
>> https://ww1.microchip.com/downloads/aemDocuments/documents/TIM/ProductDocuments/ProductBrief/ZL3063x-System-Synchronizers-with-up-to-5-Channels-10-Inputs-20-Outputs-Product-Brief-DS20006634.pdf
>>
>> https://www.sitime.com/support/resource-library/product-briefs/cascade-sit9514x-clock-system-chip-family
>>
>> https://www.ti.com/lit/ds/symlink/lmk5b33414.pdf?ts=1670516132647&ref_url=https%253A%252F%252Fwww.ti.com%252Fclocks-timing%252Fjitter-cleaners-synchronizers%252Fproducts.html
>>
>> If we model everything as "pins" we won't be able to correctly extend
>> the API to add new features.
>>
>> Sources can configure the expected frequency, input signal monitoring
>> (on multiple layers), expected signal levels, input termination and so
>> on. Outputs will need the enable flag, signal format, frequency, phase
>> offset etc. Multiple DPLLs can reuse a single source inside the same
>> package simultaneously.
> 
> 
> Looking at the documentation of the chips, they all have mupltiple DPLLs
> on a die. Arkadiusz, in your proposed implementation, do you model each
> DPLL separatelly? If yes, then I understand the urgency of need of a
> shared pin. So all DPLLs sharing the pin are part of the same chip?
> 
> Question: can we have an entity, that would be 1:1 mapped to the actual
> device/chip here? Let's call is "a synchronizer". It would contain
> multiple DPLLs, user-facing-sources(input_connector),
> user-facing-outputs(output_connector), i/o pins.
> 
> An example:
>                                SYNCHRONIZER
> 
>                               ┌───────────────────────────────────────┐
>                               │                                       │
>                               │                                       │
>   SyncE in connector          │              ┌─────────┐              │     SyncE out connector
>                 ┌───┐         │in pin 1      │DPLL_1   │     out pin 1│    ┌───┐
>                 │   ├─────────┼──────────────┤         ├──────────────┼────┤   │
>                 │   │         │              │         │              │    │   │
>                 └───┘         │              │         │              │    └───┘
>                               │              │         │              │
>                               │           ┌──┤         │              │
>    GNSS in connector          │           │  └─────────┘              │
>                 ┌───┐         │in pin 2   │                  out pin 2│     EXT SMA connector
>                 │   ├─────────┼───────────┘                           │    ┌───┐
>                 │   │         │                           ┌───────────┼────┤   │
>                 └───┘         │                           │           │    │   │
>                               │                           │           │    └───┘
>                               │                           │           │
>    EXT SMA connector          │                           │           │
>                 ┌───┐   mux   │in pin 3      ┌─────────┐  │           │
>                 │   ├────┬────┼───────────┐  │         │  │           │
>                 │   │    │    │           │  │DPLL_2   │  │           │
>                 └───┘    │    │           │  │         │  │           │
>                          │    │           └──┤         ├──┘           │
>                          │    │              │         │              │
>    EXT SMA connector     │    │              │         │              │
>                 ┌───┐    │    │              │         │              │
>                 │   ├────┘    │              └─────────┘              │
>                 │   │         │                                       │
>                 └───┘         └───────────────────────────────────────┘
> 
> Do I get that remotelly correct?

It looks goot, hence two corrections are needed:
- all inputs can go to all DPLLs, and a single source can drive more
  than one DPLL
- The external mux for SMA connector should not be a part of the
  Synchronizer subsystem - I believe there's already a separate MUX
  subsystem in the kernel and all external connections should be handled
  by a devtree or a similar concept.

The only "muxing" thing that could potentially be modeled is a
synchronizer output to synchronizer input relation. Some synchronizers
does that internally and can use the output of one DPLL as a source for
another.

Also, in theory, the DPLL->output relation may change, however I assume
we can skip support for that at the beginning.

So something like this would be roughly correct:
       ┌───────────────────────────┐
       │                           │
┌──┐   │ src0   ┌─────────┐   out0 │    ┌──┐
│  ├───┼────────┤ DPLL1   ├────────┼────┤  │
└──┘   │        │         │        │    └──┘
       │        │         │        │
       │        │         │   out1 │    ┌──┐
┌──┐   │ src1   │         ├───┬────┼────┤  │
│  ├───┼──┬─────┤         │   │    │    └──┘
└──┘   │  │     └─────────┘   │    │
       │  │   ┌───────────────┘    │
       │  │   │   src_dpll1        │
       │  │   │ ┌─────────┐   out2 │    ┌──┐
       │  │   └─┤ DPLL2   ├────────┼────┤  │
       │  │     │         │        │    └──┘
       │  └─────┤         │        │
┌──┐   │ src2   │         │        │
│  ├───┼────────┤         │        │
└──┘   │        │         │        │
       │        └─────────┘        │
       │                           │
       │                           │
       │                           │
       └───────────────────────────┘

> synch
> synchronizer_register(synch)
>    dpll_1
>    synchronizer_dpll_register(synch, dpll_1)
>    dpll_2
>    synchronizer_dpll_register(synch, dpll_2)
>    source_pin_1
>    synchronizer_pin_register(synch, source_pin_1)
>    output_pin_1
>    synchronizer_pin_register(synch, output_pin_1)
>    output_pin_2
>    synchronizer_pin_register(synch, output_pin_2)
> 
> synch_board
>    synchronizer_board_register(synch_board)
>    synch
>    synchronizer_board_sync_register(synch_board, synch)
>    source_connector_1
>    synchronizer_board_connector_register(synch_board, source_connector_1, source_pin_1)
>    output_connector_1
>    synchronizer_board_connector_register(synch_board, output_connector_1, output_pin_1)
>    output_connector_2
>    synchronizer_board_connector_register(synch_board, output_connector_2, output_pin_2)

I'd rather not use pins at all - just stick to sources and outputs. Both
can use some labels to be identifiable.


> Thinking about it a bit more, this should be probably good to describe
> by device tree. The synchronizer itself dplls and pins it contains
> have constanc geometry, according to the synchronizer device type.
> 
> The Connector-pin linkages may vary according to the board.
> 
> So to divide it, there should be one synchronizer driver. Then probably
> some other one to connect/select/mux the connectors to the synchronizer.

Agreed - we should not model external board connections inside the
synchronizer driver subsystem.
-Maciek


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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-09  9:29                                   ` Jiri Pirko
@ 2022-12-09 16:19                                     ` Jakub Kicinski
  2022-12-12 13:36                                       ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-09 16:19 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

On Fri, 9 Dec 2022 10:29:53 +0100 Jiri Pirko wrote:
> Thu, Dec 08, 2022 at 06:05:17PM CET, kuba@kernel.org wrote:
> >On Thu, 8 Dec 2022 17:33:28 +0100 Jiri Pirko wrote:  
> >> For any synce pin manipulation over dpll netlink, we can use the netns
> >> check of the linked netdev. This is the netns aware leg of the dpll,
> >> it should be checked for.  
> >
> >The OCP card is an atomic clock, it does not have any networking.  
> 
> Sure, so why it has to be netns aware if it has nothing to do with
> networking?

That's a larger question, IDK if broadening the scope of the discussion
will help us reach a conclusion. 

The patchset as is uses network namespaces for permissions:

+		.flags	= GENL_UNS_ADMIN_PERM,

so that's what I'm commenting on - aligning visibility of objects with
already used permissions.

> >> I can't imagine practically havind the whole dpll instance netns aware.
> >> Omitting the fact that it really has no meaning for non-synce pins, what
> >> would be the behaviour when for example pin 1 is in netns a, pin 2 in
> >> netns b and dpll itself in netns c?  
> >
> >To be clear I don't think it's a bad idea in general, I've done 
> >the same thing for my WIP PSP patches. But we already have one
> >device without netdevs, hence I thought maybe devlink. So maybe
> >we do the same thing with devlink? I mean - allow multiple devlink
> >instances to be linked and require caps on any of them?  
> 
> I read this 5 times, I'm lost, don't understand what you mean :/

Sorry I was replying to both paragraphs here, sorry.
What I thought you suggested is we scope the DPLL to whatever the
linked netdevs are scoped to? If netns has any of the netdevs attached
to the DPLL then it can see the DPLL and control it as well.

What I was saying is some DPLL have no netdevs. So we can do the same
thing with devlinks. Let the driver link the DPLL to one or more
devlink instances, and if any of the devlink instances is in current
netns then you can see the DPLL.

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-09 14:09                 ` Maciek Machnikowski
@ 2022-12-09 16:31                   ` Jakub Kicinski
  2022-12-09 17:11                     ` Maciek Machnikowski
  2022-12-12 13:58                     ` Jiri Pirko
  0 siblings, 2 replies; 87+ messages in thread
From: Jakub Kicinski @ 2022-12-09 16:31 UTC (permalink / raw)
  To: Maciek Machnikowski
  Cc: Jiri Pirko, 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

On Fri, 9 Dec 2022 15:09:08 +0100 Maciek Machnikowski wrote:
> On 12/9/2022 12:07 PM, Jiri Pirko wrote:
> > Looking at the documentation of the chips, they all have mupltiple DPLLs
> > on a die. Arkadiusz, in your proposed implementation, do you model each
> > DPLL separatelly? If yes, then I understand the urgency of need of a
> > shared pin. So all DPLLs sharing the pin are part of the same chip?
> > 
> > Question: can we have an entity, that would be 1:1 mapped to the actual
> > device/chip here? Let's call is "a synchronizer". It would contain
> > multiple DPLLs, user-facing-sources(input_connector),
> > user-facing-outputs(output_connector), i/o pins.
> > 
> > An example:
> >                                SYNCHRONIZER
> > 
> >                               ┌───────────────────────────────────────┐
> >                               │                                       │
> >                               │                                       │
> >   SyncE in connector          │              ┌─────────┐              │     SyncE out connector
> >                 ┌───┐         │in pin 1      │DPLL_1   │     out pin 1│    ┌───┐
> >                 │   ├─────────┼──────────────┤         ├──────────────┼────┤   │
> >                 │   │         │              │         │              │    │   │
> >                 └───┘         │              │         │              │    └───┘
> >                               │              │         │              │
> >                               │           ┌──┤         │              │
> >    GNSS in connector          │           │  └─────────┘              │
> >                 ┌───┐         │in pin 2   │                  out pin 2│     EXT SMA connector
> >                 │   ├─────────┼───────────┘                           │    ┌───┐
> >                 │   │         │                           ┌───────────┼────┤   │
> >                 └───┘         │                           │           │    │   │
> >                               │                           │           │    └───┘
> >                               │                           │           │
> >    EXT SMA connector          │                           │           │
> >                 ┌───┐   mux   │in pin 3      ┌─────────┐  │           │
> >                 │   ├────┬────┼───────────┐  │         │  │           │
> >                 │   │    │    │           │  │DPLL_2   │  │           │
> >                 └───┘    │    │           │  │         │  │           │
> >                          │    │           └──┤         ├──┘           │
> >                          │    │              │         │              │
> >    EXT SMA connector     │    │              │         │              │
> >                 ┌───┐    │    │              │         │              │
> >                 │   ├────┘    │              └─────────┘              │
> >                 │   │         │                                       │
> >                 └───┘         └───────────────────────────────────────┘
> > 
> > Do I get that remotelly correct?  
> 
> It looks goot, hence two corrections are needed:
> - all inputs can go to all DPLLs, and a single source can drive more
>   than one DPLL
> - The external mux for SMA connector should not be a part of the
>   Synchronizer subsystem - I believe there's already a separate MUX
>   subsystem in the kernel and all external connections should be handled
>   by a devtree or a similar concept.
> 
> The only "muxing" thing that could potentially be modeled is a
> synchronizer output to synchronizer input relation. Some synchronizers
> does that internally and can use the output of one DPLL as a source for
> another.

My experience with DT and muxes is rapidly aging, have you worked with
those recently? From what I remember the muxes were really.. "embedded"
and static compared to what we want here.

Using DT may work nicely for defining the topology, but for config we
still need a different mechanism.

> > synch
> > synchronizer_register(synch)
> >    dpll_1
> >    synchronizer_dpll_register(synch, dpll_1)
> >    dpll_2
> >    synchronizer_dpll_register(synch, dpll_2)
> >    source_pin_1
> >    synchronizer_pin_register(synch, source_pin_1)
> >    output_pin_1
> >    synchronizer_pin_register(synch, output_pin_1)
> >    output_pin_2
> >    synchronizer_pin_register(synch, output_pin_2)
> > 
> > synch_board
> >    synchronizer_board_register(synch_board)
> >    synch
> >    synchronizer_board_sync_register(synch_board, synch)
> >    source_connector_1
> >    synchronizer_board_connector_register(synch_board, source_connector_1, source_pin_1)
> >    output_connector_1
> >    synchronizer_board_connector_register(synch_board, output_connector_1, output_pin_1)
> >    output_connector_2
> >    synchronizer_board_connector_register(synch_board, output_connector_2, output_pin_2)  
> 
> I'd rather not use pins at all - just stick to sources and outputs. Both
> can use some labels to be identifiable.

TBH I can't comprehend your suggestion.
IIUC you want an object for a source, but my brain can't handle
modeling an external object. For instance the source could be GNSS, 
but this is not the GNSS subsystem. We have a pin connected to GNSS,
not the GNSS itself. 
Maybe a diagram would help?

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-09 16:31                   ` Jakub Kicinski
@ 2022-12-09 17:11                     ` Maciek Machnikowski
  2022-12-12 13:58                     ` Jiri Pirko
  1 sibling, 0 replies; 87+ messages in thread
From: Maciek Machnikowski @ 2022-12-09 17:11 UTC (permalink / raw)
  To: Jakub Kicinski, Maciek Machnikowski
  Cc: Jiri Pirko, 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk



On 12/9/2022 5:31 PM, Jakub Kicinski wrote:
> On Fri, 9 Dec 2022 15:09:08 +0100 Maciek Machnikowski wrote:
>> On 12/9/2022 12:07 PM, Jiri Pirko wrote:
>>> Looking at the documentation of the chips, they all have mupltiple DPLLs
>>> on a die. Arkadiusz, in your proposed implementation, do you model each
>>> DPLL separatelly? If yes, then I understand the urgency of need of a
>>> shared pin. So all DPLLs sharing the pin are part of the same chip?
>>>
>>> Question: can we have an entity, that would be 1:1 mapped to the actual
>>> device/chip here? Let's call is "a synchronizer". It would contain
>>> multiple DPLLs, user-facing-sources(input_connector),
>>> user-facing-outputs(output_connector), i/o pins.
>>>
>>> An example:
>>>                                SYNCHRONIZER
>>>
>>>                               ┌───────────────────────────────────────┐
>>>                               │                                       │
>>>                               │                                       │
>>>   SyncE in connector          │              ┌─────────┐              │     SyncE out connector
>>>                 ┌───┐         │in pin 1      │DPLL_1   │     out pin 1│    ┌───┐
>>>                 │   ├─────────┼──────────────┤         ├──────────────┼────┤   │
>>>                 │   │         │              │         │              │    │   │
>>>                 └───┘         │              │         │              │    └───┘
>>>                               │              │         │              │
>>>                               │           ┌──┤         │              │
>>>    GNSS in connector          │           │  └─────────┘              │
>>>                 ┌───┐         │in pin 2   │                  out pin 2│     EXT SMA connector
>>>                 │   ├─────────┼───────────┘                           │    ┌───┐
>>>                 │   │         │                           ┌───────────┼────┤   │
>>>                 └───┘         │                           │           │    │   │
>>>                               │                           │           │    └───┘
>>>                               │                           │           │
>>>    EXT SMA connector          │                           │           │
>>>                 ┌───┐   mux   │in pin 3      ┌─────────┐  │           │
>>>                 │   ├────┬────┼───────────┐  │         │  │           │
>>>                 │   │    │    │           │  │DPLL_2   │  │           │
>>>                 └───┘    │    │           │  │         │  │           │
>>>                          │    │           └──┤         ├──┘           │
>>>                          │    │              │         │              │
>>>    EXT SMA connector     │    │              │         │              │
>>>                 ┌───┐    │    │              │         │              │
>>>                 │   ├────┘    │              └─────────┘              │
>>>                 │   │         │                                       │
>>>                 └───┘         └───────────────────────────────────────┘
>>>
>>> Do I get that remotelly correct?  
>>
>> It looks goot, hence two corrections are needed:
>> - all inputs can go to all DPLLs, and a single source can drive more
>>   than one DPLL
>> - The external mux for SMA connector should not be a part of the
>>   Synchronizer subsystem - I believe there's already a separate MUX
>>   subsystem in the kernel and all external connections should be handled
>>   by a devtree or a similar concept.
>>
>> The only "muxing" thing that could potentially be modeled is a
>> synchronizer output to synchronizer input relation. Some synchronizers
>> does that internally and can use the output of one DPLL as a source for
>> another.
> 
> My experience with DT and muxes is rapidly aging, have you worked with
> those recently? From what I remember the muxes were really.. "embedded"
> and static compared to what we want here.
> 
> Using DT may work nicely for defining the topology, but for config we
> still need a different mechanism.
> 
>>> synch
>>> synchronizer_register(synch)
>>>    dpll_1
>>>    synchronizer_dpll_register(synch, dpll_1)
>>>    dpll_2
>>>    synchronizer_dpll_register(synch, dpll_2)
>>>    source_pin_1
>>>    synchronizer_pin_register(synch, source_pin_1)
>>>    output_pin_1
>>>    synchronizer_pin_register(synch, output_pin_1)
>>>    output_pin_2
>>>    synchronizer_pin_register(synch, output_pin_2)
>>>
>>> synch_board
>>>    synchronizer_board_register(synch_board)
>>>    synch
>>>    synchronizer_board_sync_register(synch_board, synch)
>>>    source_connector_1
>>>    synchronizer_board_connector_register(synch_board, source_connector_1, source_pin_1)
>>>    output_connector_1
>>>    synchronizer_board_connector_register(synch_board, output_connector_1, output_pin_1)
>>>    output_connector_2
>>>    synchronizer_board_connector_register(synch_board, output_connector_2, output_pin_2)  
>>
>> I'd rather not use pins at all - just stick to sources and outputs. Both
>> can use some labels to be identifiable.
> 
> TBH I can't comprehend your suggestion.
> IIUC you want an object for a source, but my brain can't handle
> modeling an external object. For instance the source could be GNSS, 
> but this is not the GNSS subsystem. We have a pin connected to GNSS,
> not the GNSS itself. 
> Maybe a diagram would help?

A source is just a more generic term for a frequency signal that can be
used by a DPLL. For some solutions it can represent a pin, for others
(integrated) it can represent an internal connection to a different
DPLL/PHY/MAC/embedded oscillator or anything else that can produce
periodic signal.

This object will have a subset of properties listed in a previous mail:
>>>> Sources can configure the expected frequency, input signal
>>>> monitoring (on multiple layers), expected signal levels, input
>>>> termination and so on. Outputs will need the enable flag, signal
>>>> format, frequency, phase offset etc. Multiple DPLLs can reuse a
>>>> single source inside the same package simultaneously.

I'm absolutely not willing to connect the GNSS subsystem there :)

A "pin" is too ambiguous - especially for differential inputs.


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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-09 16:19                                     ` Jakub Kicinski
@ 2022-12-12 13:36                                       ` Jiri Pirko
  2022-12-13 18:08                                         ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-12 13:36 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Vadim Fedorenko, Jonathan Lemon,
	Paolo Abeni, netdev, Vadim Fedorenko, linux-arm-kernel,
	linux-clk, Olech, Milena, Michalik, Michal

Fri, Dec 09, 2022 at 05:19:42PM CET, kuba@kernel.org wrote:
>On Fri, 9 Dec 2022 10:29:53 +0100 Jiri Pirko wrote:
>> Thu, Dec 08, 2022 at 06:05:17PM CET, kuba@kernel.org wrote:
>> >On Thu, 8 Dec 2022 17:33:28 +0100 Jiri Pirko wrote:  
>> >> For any synce pin manipulation over dpll netlink, we can use the netns
>> >> check of the linked netdev. This is the netns aware leg of the dpll,
>> >> it should be checked for.  
>> >
>> >The OCP card is an atomic clock, it does not have any networking.  
>> 
>> Sure, so why it has to be netns aware if it has nothing to do with
>> networking?
>
>That's a larger question, IDK if broadening the scope of the discussion
>will help us reach a conclusion. 
>
>The patchset as is uses network namespaces for permissions:
>
>+		.flags	= GENL_UNS_ADMIN_PERM,

Yeah, I wonder if just GENL_ADMIN_PERM wuldn't be more suitable here...


>
>so that's what I'm commenting on - aligning visibility of objects with
>already used permissions.
>
>> >> I can't imagine practically havind the whole dpll instance netns aware.
>> >> Omitting the fact that it really has no meaning for non-synce pins, what
>> >> would be the behaviour when for example pin 1 is in netns a, pin 2 in
>> >> netns b and dpll itself in netns c?  
>> >
>> >To be clear I don't think it's a bad idea in general, I've done 
>> >the same thing for my WIP PSP patches. But we already have one
>> >device without netdevs, hence I thought maybe devlink. So maybe
>> >we do the same thing with devlink? I mean - allow multiple devlink
>> >instances to be linked and require caps on any of them?  
>> 
>> I read this 5 times, I'm lost, don't understand what you mean :/
>
>Sorry I was replying to both paragraphs here, sorry.
>What I thought you suggested is we scope the DPLL to whatever the
>linked netdevs are scoped to? If netns has any of the netdevs attached
>to the DPLL then it can see the DPLL and control it as well.

Okay, that would make sense.
GENL_UNS_ADMIN_PERM | GENL_UNS_ADMIN_PERM
then.

>
>What I was saying is some DPLL have no netdevs. So we can do the same
>thing with devlinks. Let the driver link the DPLL to one or more
>devlink instances, and if any of the devlink instances is in current
>netns then you can see the DPLL.

I don't think that would be needed to pull devlink into the picture.
If not netdev is linked to dpll, GENL_ADMIN_PERM would apply.


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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-09 16:31                   ` Jakub Kicinski
  2022-12-09 17:11                     ` Maciek Machnikowski
@ 2022-12-12 13:58                     ` Jiri Pirko
  2023-01-09 14:43                       ` Kubalewski, Arkadiusz
  1 sibling, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2022-12-12 13:58 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Maciek Machnikowski, 'Kubalewski, Arkadiusz',
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

Fri, Dec 09, 2022 at 05:31:04PM CET, kuba@kernel.org wrote:
>On Fri, 9 Dec 2022 15:09:08 +0100 Maciek Machnikowski wrote:
>> On 12/9/2022 12:07 PM, Jiri Pirko wrote:
>> > Looking at the documentation of the chips, they all have mupltiple DPLLs
>> > on a die. Arkadiusz, in your proposed implementation, do you model each
>> > DPLL separatelly? If yes, then I understand the urgency of need of a
>> > shared pin. So all DPLLs sharing the pin are part of the same chip?
>> > 
>> > Question: can we have an entity, that would be 1:1 mapped to the actual
>> > device/chip here? Let's call is "a synchronizer". It would contain
>> > multiple DPLLs, user-facing-sources(input_connector),
>> > user-facing-outputs(output_connector), i/o pins.
>> > 
>> > An example:
>> >                                SYNCHRONIZER
>> > 
>> >                               ┌───────────────────────────────────────┐
>> >                               │                                       │
>> >                               │                                       │
>> >   SyncE in connector          │              ┌─────────┐              │     SyncE out connector
>> >                 ┌───┐         │in pin 1      │DPLL_1   │     out pin 1│    ┌───┐
>> >                 │   ├─────────┼──────────────┤         ├──────────────┼────┤   │
>> >                 │   │         │              │         │              │    │   │
>> >                 └───┘         │              │         │              │    └───┘
>> >                               │              │         │              │
>> >                               │           ┌──┤         │              │
>> >    GNSS in connector          │           │  └─────────┘              │
>> >                 ┌───┐         │in pin 2   │                  out pin 2│     EXT SMA connector
>> >                 │   ├─────────┼───────────┘                           │    ┌───┐
>> >                 │   │         │                           ┌───────────┼────┤   │
>> >                 └───┘         │                           │           │    │   │
>> >                               │                           │           │    └───┘
>> >                               │                           │           │
>> >    EXT SMA connector          │                           │           │
>> >                 ┌───┐   mux   │in pin 3      ┌─────────┐  │           │
>> >                 │   ├────┬────┼───────────┐  │         │  │           │
>> >                 │   │    │    │           │  │DPLL_2   │  │           │
>> >                 └───┘    │    │           │  │         │  │           │
>> >                          │    │           └──┤         ├──┘           │
>> >                          │    │              │         │              │
>> >    EXT SMA connector     │    │              │         │              │
>> >                 ┌───┐    │    │              │         │              │
>> >                 │   ├────┘    │              └─────────┘              │
>> >                 │   │         │                                       │
>> >                 └───┘         └───────────────────────────────────────┘
>> > 
>> > Do I get that remotelly correct?  
>> 
>> It looks goot, hence two corrections are needed:
>> - all inputs can go to all DPLLs, and a single source can drive more
>>   than one DPLL
>> - The external mux for SMA connector should not be a part of the
>>   Synchronizer subsystem - I believe there's already a separate MUX
>>   subsystem in the kernel and all external connections should be handled
>>   by a devtree or a similar concept.
>> 
>> The only "muxing" thing that could potentially be modeled is a
>> synchronizer output to synchronizer input relation. Some synchronizers
>> does that internally and can use the output of one DPLL as a source for
>> another.
>
>My experience with DT and muxes is rapidly aging, have you worked with
>those recently? From what I remember the muxes were really.. "embedded"
>and static compared to what we want here.

Why do you think we need something "non-static"? The mux is part of the
board, isn't it? That sounds quite static to me.


>
>Using DT may work nicely for defining the topology, but for config we
>still need a different mechanism.

"config" of what? Each item in topology would be configure according to
the item type, won't it?

[...]

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

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-12 13:36                                       ` Jiri Pirko
@ 2022-12-13 18:08                                         ` Kubalewski, Arkadiusz
  2022-12-14  7:32                                           ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-13 18:08 UTC (permalink / raw)
  To: Jiri Pirko, Jakub Kicinski
  Cc: Vadim Fedorenko, Jonathan Lemon, Paolo Abeni, netdev,
	Vadim Fedorenko, linux-arm-kernel, linux-clk, Olech, Milena,
	Michalik, Michal

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, December 12, 2022 2:37 PM
>To: Jakub Kicinski <kuba@kernel.org>
>
>Fri, Dec 09, 2022 at 05:19:42PM CET, kuba@kernel.org wrote:
>>On Fri, 9 Dec 2022 10:29:53 +0100 Jiri Pirko wrote:
>>> Thu, Dec 08, 2022 at 06:05:17PM CET, kuba@kernel.org wrote:
>>> >On Thu, 8 Dec 2022 17:33:28 +0100 Jiri Pirko wrote:
>>> >> For any synce pin manipulation over dpll netlink, we can use the
>>> >> netns check of the linked netdev. This is the netns aware leg of
>>> >> the dpll, it should be checked for.
>>> >
>>> >The OCP card is an atomic clock, it does not have any networking.
>>>
>>> Sure, so why it has to be netns aware if it has nothing to do with
>>> networking?
>>
>>That's a larger question, IDK if broadening the scope of the discussion
>>will help us reach a conclusion.
>>
>>The patchset as is uses network namespaces for permissions:
>>
>>+		.flags	= GENL_UNS_ADMIN_PERM,
>
>Yeah, I wonder if just GENL_ADMIN_PERM wuldn't be more suitable here...
>
>
>>
>>so that's what I'm commenting on - aligning visibility of objects with
>>already used permissions.
>>
>>> >> I can't imagine practically havind the whole dpll instance netns
>aware.
>>> >> Omitting the fact that it really has no meaning for non-synce
>>> >> pins, what would be the behaviour when for example pin 1 is in
>>> >> netns a, pin 2 in netns b and dpll itself in netns c?
>>> >
>>> >To be clear I don't think it's a bad idea in general, I've done the
>>> >same thing for my WIP PSP patches. But we already have one device
>>> >without netdevs, hence I thought maybe devlink. So maybe we do the
>>> >same thing with devlink? I mean - allow multiple devlink instances
>>> >to be linked and require caps on any of them?
>>>
>>> I read this 5 times, I'm lost, don't understand what you mean :/
>>
>>Sorry I was replying to both paragraphs here, sorry.
>>What I thought you suggested is we scope the DPLL to whatever the
>>linked netdevs are scoped to? If netns has any of the netdevs attached
>>to the DPLL then it can see the DPLL and control it as well.
>
>Okay, that would make sense.
>GENL_UNS_ADMIN_PERM | GENL_UNS_ADMIN_PERM then.
>

I guess a typo here? Shall be: 'GENL_UNS_ADMIN_PERM | GENL_ADMIN_PERM'?
Going to:
- apply those bits for all the dpll netlink commands,
- remove DPLLA_NETIFINDEX,
- leave pin DPLLA_PIN_NETIFINDEX as is.

Or I have missed something?

Thanks,
Arkadiusz

>>
>>What I was saying is some DPLL have no netdevs. So we can do the same
>>thing with devlinks. Let the driver link the DPLL to one or more
>>devlink instances, and if any of the devlink instances is in current
>>netns then you can see the DPLL.
>
>I don't think that would be needed to pull devlink into the picture.
>If not netdev is linked to dpll, GENL_ADMIN_PERM would apply.


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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-12-13 18:08                                         ` Kubalewski, Arkadiusz
@ 2022-12-14  7:32                                           ` Jiri Pirko
  0 siblings, 0 replies; 87+ messages in thread
From: Jiri Pirko @ 2022-12-14  7:32 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Vadim Fedorenko, Jonathan Lemon, Paolo Abeni,
	netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk, Olech,
	Milena, Michalik, Michal

Tue, Dec 13, 2022 at 07:08:13PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, December 12, 2022 2:37 PM
>>To: Jakub Kicinski <kuba@kernel.org>
>>
>>Fri, Dec 09, 2022 at 05:19:42PM CET, kuba@kernel.org wrote:
>>>On Fri, 9 Dec 2022 10:29:53 +0100 Jiri Pirko wrote:
>>>> Thu, Dec 08, 2022 at 06:05:17PM CET, kuba@kernel.org wrote:
>>>> >On Thu, 8 Dec 2022 17:33:28 +0100 Jiri Pirko wrote:
>>>> >> For any synce pin manipulation over dpll netlink, we can use the
>>>> >> netns check of the linked netdev. This is the netns aware leg of
>>>> >> the dpll, it should be checked for.
>>>> >
>>>> >The OCP card is an atomic clock, it does not have any networking.
>>>>
>>>> Sure, so why it has to be netns aware if it has nothing to do with
>>>> networking?
>>>
>>>That's a larger question, IDK if broadening the scope of the discussion
>>>will help us reach a conclusion.
>>>
>>>The patchset as is uses network namespaces for permissions:
>>>
>>>+		.flags	= GENL_UNS_ADMIN_PERM,
>>
>>Yeah, I wonder if just GENL_ADMIN_PERM wuldn't be more suitable here...
>>
>>
>>>
>>>so that's what I'm commenting on - aligning visibility of objects with
>>>already used permissions.
>>>
>>>> >> I can't imagine practically havind the whole dpll instance netns
>>aware.
>>>> >> Omitting the fact that it really has no meaning for non-synce
>>>> >> pins, what would be the behaviour when for example pin 1 is in
>>>> >> netns a, pin 2 in netns b and dpll itself in netns c?
>>>> >
>>>> >To be clear I don't think it's a bad idea in general, I've done the
>>>> >same thing for my WIP PSP patches. But we already have one device
>>>> >without netdevs, hence I thought maybe devlink. So maybe we do the
>>>> >same thing with devlink? I mean - allow multiple devlink instances
>>>> >to be linked and require caps on any of them?
>>>>
>>>> I read this 5 times, I'm lost, don't understand what you mean :/
>>>
>>>Sorry I was replying to both paragraphs here, sorry.
>>>What I thought you suggested is we scope the DPLL to whatever the
>>>linked netdevs are scoped to? If netns has any of the netdevs attached
>>>to the DPLL then it can see the DPLL and control it as well.
>>
>>Okay, that would make sense.
>>GENL_UNS_ADMIN_PERM | GENL_UNS_ADMIN_PERM then.
>>
>
>I guess a typo here? Shall be: 'GENL_UNS_ADMIN_PERM | GENL_ADMIN_PERM'?

Yes, sure.

>Going to:
>- apply those bits for all the dpll netlink commands,
>- remove DPLLA_NETIFINDEX,
>- leave pin DPLLA_PIN_NETIFINDEX as is.
>
>Or I have missed something?

I believe it is ok.

>
>Thanks,
>Arkadiusz
>
>>>
>>>What I was saying is some DPLL have no netdevs. So we can do the same
>>>thing with devlinks. Let the driver link the DPLL to one or more
>>>devlink instances, and if any of the devlink instances is in current
>>>netns then you can see the DPLL.
>>
>>I don't think that would be needed to pull devlink into the picture.
>>If not netdev is linked to dpll, GENL_ADMIN_PERM would apply.
>

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

* Re: [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
  2022-11-29 21:37 ` [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface Vadim Fedorenko
@ 2022-12-19  9:13   ` Paolo Abeni
  2023-01-12 13:45     ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Paolo Abeni @ 2022-12-19  9:13 UTC (permalink / raw)
  To: Vadim Fedorenko, Jakub Kicinski, Jiri Pirko,
	Arkadiusz Kubalewski, Jonathan Lemon
  Cc: netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

Hello,

I have a just a few minor notes WRT the documentation - which was a
very useful entry point for me to help understanding the subsystem.

On Wed, 2022-11-30 at 00:37 +0300, Vadim Fedorenko wrote:
> From: Vadim Fedorenko <vadfed@fb.com>
> 
> 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@fb.com>
> ---
>  Documentation/networking/dpll.rst  | 271 +++++++++++++++++++++++++++++
>  Documentation/networking/index.rst |   1 +
>  2 files changed, 272 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..58401e2b70a7
> --- /dev/null
> +++ b/Documentation/networking/dpll.rst
> @@ -0,0 +1,271 @@
> +.. 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_SGINAL_TYPE, DPLL_PIN_TYPE,

Likely typo above: DPLL_PIN_SIGNAL_TYPE

> +DPLL_PIN_STATE), 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.

Side note: in the long run we could end-up with a virtual/dummy dpll
driver for self-tests and/or reference's implementation sake.

> +
> +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
> +    ``NETIFINDEX``              attr dpll owner Linux netdevice index

should we include also the cookie (or wuhatever will be used for
persistent device identification) into the readable attributes list? 

> +  ``DEVICE_SET``                userspace to set dpll device
> +                                configuration
> +    ``ID``                      attr internal dpll device index
> +    ``MODE``                    attr selection mode to configure
> +    ``PIN_IDX``                 attr index of source pin to select as
> +                                active source

It looks like the descrition for the above attribute ('PIN_IDX') and
'SOURCE_PIN_IDX' has been swapped.

> +  ``PIN_SET``                   userspace to set pins configuration
> +    ``ID``                      attr internal dpll device index
> +    ``PIN_IDX``                 attr index of a pin to configure
> +    ``PIN_TYPE``                attr type configuration value for
> +                                selected pin
> +    ``PIN_SIGNAL_TYPE``         attr signal type configuration value
> +                                for selected pin
> +    ``PIN_CUSTOM_FREQ``         attr signal custom frequency to be set
> +    ``PIN_STATE``               attr pin state to be set
> +    ``PIN_PRIO``                attr pin priority to be set
> +
> +Netlink dump requests
> +=====================
> +The ``DEVICE_GET`` command is capable of dump type netlink requests.
> +In such case the userspace shall provide ``DUMP_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.

Should we explicitly document even the required permissions?

> +
> +Possible response message attributes for netlink requests depending on
> +the value of ``DPLLA_DUMP_FILTER`` attribute:
> +
> +  =============================== ====================================
> +  ``DPLL_DUMP_FILTER_PINS``       value of ``DUMP_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_TYPE_SUPPORTED``        attr value of supported 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_STATE``                 attr value of pin state
> +    ``PIN_STATE_SUPPORTED``       attr value of supported pin state
> +    ``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_DUMP_FILTER_STATUS``     value of ``DUMP_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
> +    ``NETIFINDEX``                attr dpll owner Linux netdevice index
> +
> +
> +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_STATE`` and ``PIN_STATE_SUPPORTED`` attributes:
> +
> +============================= ============================
> +  ``PIN_STATE_CONNECTED``     Pin connected to a dpll
> +  ``PIN_STATE_DISCONNECTED``  Pin disconnected from dpll
> +  ``PIN_STATE_SOURCE``        Source pin
> +  ``PIN_STATE_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 ise auto selected according to

typo above 'ise' -> 'is'


Cheers,

Paolo


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

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-30 15:21   ` Jiri Pirko
  2022-11-30 16:23     ` Jiri Pirko
@ 2022-12-23 16:45     ` Kubalewski, Arkadiusz
  2023-01-02 12:28       ` Jiri Pirko
  1 sibling, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2022-12-23 16:45 UTC (permalink / raw)
  To: Jiri Pirko, Vadim Fedorenko
  Cc: Jakub Kicinski, Jonathan Lemon, Paolo Abeni, netdev,
	Vadim Fedorenko, linux-arm-kernel, linux-clk, Olech, Milena,
	Michalik, Michal

>-----Original Message-----
>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, November 30, 2022 4:21 PM
>
>Tue, Nov 29, 2022 at 10:37:22PM CET, vfedorenko@novek.ru wrote:
>>From: Vadim Fedorenko <vadfed@fb.com>
>>
>>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@fb.com>
>>---
>> MAINTAINERS                 |   8 +
>> drivers/Kconfig             |   2 +
>> drivers/Makefile            |   1 +
>> drivers/dpll/Kconfig        |   7 +
>> drivers/dpll/Makefile       |  11 +
>> drivers/dpll/dpll_core.c    | 760 ++++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h    | 176 +++++++
>> drivers/dpll/dpll_netlink.c | 963 ++++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h |  24 +
>> include/linux/dpll.h        | 261 ++++++++++
>> include/uapi/linux/dpll.h   | 263 ++++++++++
>> 11 files changed, 2476 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 61fe86968111..79b76cca9620 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -6343,6 +6343,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 19ee995bd0ae..a3e00294a995 100644
>>--- a/drivers/Kconfig
>>+++ b/drivers/Kconfig
>>@@ -239,4 +239,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..3391deb08014
>>--- /dev/null
>>+++ b/drivers/dpll/Makefile
>>@@ -0,0 +1,11 @@
>>+# SPDX-License-Identifier: GPL-2.0
>>+#
>>+# Makefile for DPLL drivers.
>>+#
>>+
>>+obj-$(CONFIG_DPLL)          += dpll_sys.o
>>+dpll_sys-y                  += dpll_attr.o
>>+dpll_sys-y                  += dpll_core.o
>>+dpll_sys-y                  += dpll_netlink.o
>>+dpll_sys-y                  += dpll_pin_attr.o
>>+
>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>new file mode 100644
>>index 000000000000..013ac4150583
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.c
>>@@ -0,0 +1,760 @@
>>+// 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[PIN_DESC_LEN];
>>+};
>>+
>>+/**
>>+ * 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
>>+ * @cookie:	unique identifier (cookie) of a dpll
>>+ * @dev_driver_idx: provided by driver for
>>+ */
>>+struct dpll_device {
>
>Just "dpll" please. Device somehow indicates this is managing device on
>the bus. It is misleading.
>

Left as asked in follow up email.

>
>>+	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;
>>+	u8 cookie[DPLL_COOKIE_LEN];
>>+	u8 dev_driver_idx;
>>+};
>>+
>>+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 pin_ref_dpll {
>
>Namespace
>

Gonna fix in next version.

>
>>+	struct dpll_device *dpll;
>>+	struct dpll_pin_ops *ops;
>>+	void *priv;
>>+};
>>+
>>+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;
>>+}
>>+
>>+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_cookie(u8 cookie[DPLL_COOKIE_LEN],
>>+					      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 (!memcmp(dpll->cookie, cookie, DPLL_COOKIE_LEN)) {
>>+			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_cookie);
>>+
>>+static void dpll_device_release(struct device *dev)
>>+{
>>+	struct dpll_device *dpll;
>>+
>>+	dpll = to_dpll_device(dev);
>>+
>>+	dpll_device_unregister(dpll);
>>+	dpll_device_free(dpll);
>>+}
>>+
>>+static struct class dpll_class = {
>>+	.name = "dpll",
>>+	.dev_release = dpll_device_release,
>>+};
>>+
>>+static const char *dpll_type_name[__DPLL_TYPE_MAX] = {
>>+	[DPLL_TYPE_UNSPEC] = "",
>>+	[DPLL_TYPE_PPS] = "PPS",
>>+	[DPLL_TYPE_EEC] = "EEC",
>>+};
>>+
>>+static const char *dpll_type_str(enum dpll_type type)
>>+{
>>+	if (type >= DPLL_TYPE_UNSPEC && type <= DPLL_TYPE_MAX)
>>+		return dpll_type_name[type];
>>+	else
>>+		return "";
>>+}
>>+
>>+struct dpll_device
>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>+		   const u8 cookie[DPLL_COOKIE_LEN], u8 idx,
>
>This cooking thing should be removed alongside with the getter. Driver
>should not need it.
>

Gonna fix in next version. Will replace with clock_id.

>
>>+		   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 = idx;
>>+	memcpy(dpll->cookie, cookie, sizeof(dpll->cookie));
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>
>You have idx and id? Why?
>

id is system id, idx is given by driver registering it, as for now one driver
can allocate many dplls with the same type and clock_id.

>
>>+		       xa_limit_16b, GFP_KERNEL);
>>+	if (ret)
>>+		goto error;
>>+	dev_set_name(&dpll->dev, "dpll-%s-%s-%s%d",
>dev_driver_string(parent),
>>+		     dev_name(parent), type ? dpll_type_str(type) : "", idx);
>
>Odd. You encode parent device name into a name. What do we have device
>hierarchy for? Also, why to encode "type_str"? Does no make sense to me.
>

Gonna fix in next version.

>
>>+	dpll->priv = priv;
>>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>>+	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);
>>+
>>+void dpll_device_register(struct dpll_device *dpll)
>>+{
>>+	ASSERT_DPLL_NOT_REGISTERED(dpll);
>>+
>>+	mutex_lock(&dpll_device_xa_lock);
>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>+	mutex_unlock(&dpll_device_xa_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_register);
>>+
>>+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);
>>+
>>+u32 dpll_id(struct dpll_device *dpll)
>>+{
>>+	return dpll->id;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_id);
>>+
>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)
>
>This is odd. You just need pin here, no need to pass dpll.
>

It is needed for "dpll_pin_ref dance".

>
>>+{
>>+	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);
>
>General comment to this getter helpers. Why do you need them? Why the
>driver cares about name, abou idx and other attributes. Why driver needs
>to iterate over pins?
>This should not be needed, please remove.
>

All is required to share a dplls and pins.
Separated driver instances shall be able to find dpll or pin of the other
instance.

>
>
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_dev_name);
>>+
>>+struct dpll_pin *dpll_pin_alloc(const char *description, size_t desc_len)
>
>
>Why do you need description? In your pps example, you pass "SMA". Isn't
>that rather a pin type? Const attribute of a pin?
>I can understand a need for "label", if you have 2 SMA connectors
>labeled "port 1" and "port 2", pass that.

What about U.FL? We probably could add signal types like:
- DPLL_PIN_SIGNAL_TYPE_EXT_SMA,
- DPLL_PIN_SIGNAL_TYPE_EXT_UFL.
Maybe more if there are some other viable connectors..
Then pass label/description as you suggested.

>
>Also, it's a string, you don't need len.
>Also, you can have this as vararg to make caller's live easier.
>

Sure, we are rather safe to remove desc_len, but I don't get how vararg would
be beneficial here?

>
>
>>+{
>>+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
>>+
>>+	if (!pin)
>>+		return ERR_PTR(-ENOMEM);
>>+	if (desc_len > PIN_DESC_LEN)
>>+		return ERR_PTR(-EINVAL);
>>+
>>+	strncpy(pin->description, description, PIN_DESC_LEN);
>>+	if (desc_len == PIN_DESC_LEN)
>>+		pin->description[PIN_DESC_LEN - 1] = '\0';
>>+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
>>+
>>+	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, 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 pin_ref_dpll_add(struct dpll_pin *pin, struct dpll_device
>*dpll,
>>+			    struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	struct pin_ref_dpll *ref, *pos;
>>+	unsigned long index;
>>+	u32 idx;
>>+
>>+	ref = kzalloc(sizeof(struct pin_ref_dpll), 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 pin_ref_dpll_del(struct dpll_pin *pin, struct dpll_device
>*dpll)
>>+{
>>+	struct pin_ref_dpll *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;
>>+		}
>>+	}
>>+}
>
>As I wrote in the reply to the cover, I believe the sharing of pins
>between instances is wrong, then you can avoid this ref dance.
>

I see this differently, as sharing pins and dplls is beneficial for complex
hardware designs, leaving for now.

>
>
>>+
>>+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 = pin_ref_dpll_add(pin, dpll, ops, priv);
>>+		if (ret)
>>+			pin_deregister_from_xa(&dpll->pins, pin);
>>+	}
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_ADD, pin);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>+
>>+int
>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>+			 struct dpll_device *dpll, u32 pin_idx,
>>+			 struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	struct dpll_pin *pin;
>>+	int ret;
>>+
>>+	mutex_lock(&dpll_pin_owner->lock);
>>+	pin = dpll_pin_get_by_idx(dpll_pin_owner, pin_idx);
>>+	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)
>>+		pin_ref_dpll_del(pin, dpll);
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_DEL, pin);
>>+
>>+	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,
>>+			    struct dpll_pin *parent_pin, struct dpll_pin *pin,
>>+			    struct dpll_pin_ops *ops, void *priv)
>>+{
>>+	int ret;
>>+
>>+	if (!parent_pin || !pin)
>>+		return -EINVAL;
>>+
>>+	mutex_lock(&dpll->lock);
>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>+	if (!ret)
>>+		ret = pin_ref_dpll_add(pin, dpll, ops, priv);
>>+	if (!ret)
>>+		pin->parent_pin = parent_pin;
>>+	mutex_unlock(&dpll->lock);
>>+	if (!ret)
>>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_ADD, pin);
>>+
>>+	return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
>>+
>>+struct dpll_pin
>>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char
>*description)
>
>Why on earth would driver need this helper? If it does, something is
>wrong. Please remove.
>

I.e. The driver wants to add it's recovered clock as a source for the MUX type
pin. It finds dpll, finds a MUX type pin, allocates new pin, registers it with
previously found MUX type pin.

Searching for DPLL will be done now with a clock_id (previously cookie), type
of dpll and index (all provided by driver on dpll device allocation).

For searching of a pin, I think I am going to remove it, instead will modify:
int dpll_muxed_pin_register(struct dpll_device *dpll,
                            struct dpll_pin *parent_pin, struct dpll_pin *pin,
                            struct dpll_pin_ops *ops, void *priv)
Letting the user register with MUX-type pin label, instead of its pointer.
This way searching of the pin won't be needed anymore.

>
>>+{
>>+	struct dpll_pin *pos, *pin = NULL;
>>+	unsigned long index;
>>+
>>+	mutex_lock(&dpll->lock);
>>+	xa_for_each(&dpll->pins, index, pos) {
>>+		if (!strncmp(pos->description, description, PIN_DESC_LEN)) {
>>+			pin = pos;
>>+			break;
>>+		}
>>+	}
>>+	mutex_unlock(&dpll->lock);
>>+
>>+	return pin;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_get_by_description);
>>+
>>+static 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;
>>+}
>>+
>>+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);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_get_by_idx);
>
>Again, please remove these heplers. Driver should be happy only with
>opaque struct dpll and struct dpll_pin
>

This won't be exported anymore.

>
>>+
>>+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);
>>+}
>>+
>>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long
>*index)
>>+{
>>+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>>+}
>>+
>>+struct dpll_device *dpll_first(unsigned long *index)
>>+{
>>+	*index = 0;
>>+
>>+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>>+}
>>+
>>+struct dpll_device *dpll_next(unsigned long *index)
>>+{
>>+	return xa_find_after(&dpll_device_xa, index, LONG_MAX,
>DPLL_REGISTERED);
>>+}
>>+
>>+static int
>>+dpll_notify_pin_change_attr(struct dpll_device *dpll, struct dpll_pin
>*pin,
>>+			    const struct dpll_pin_attr *attr)
>>+{
>>+	enum dpll_event_change change;
>>+	int ret = 0;
>>+
>>+	if (dpll_pin_attr_valid(DPLLA_PIN_TYPE, attr)) {
>>+		change = DPLL_CHANGE_PIN_TYPE;
>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>+	}
>>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_SIGNAL_TYPE, attr)) {
>>+		change = DPLL_CHANGE_PIN_SIGNAL_TYPE;
>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>+	}
>>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_CUSTOM_FREQ, attr)) {
>>+		change = DPLL_CHANGE_PIN_CUSTOM_FREQ;
>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>+	}
>>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_STATE, attr)) {
>>+		change = DPLL_CHANGE_PIN_STATE;
>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>+	}
>>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_PRIO, attr)) {
>>+		change = DPLL_CHANGE_PIN_PRIO;
>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_notify_device_change_attr(struct dpll_device *dpll,
>>+					  const struct dpll_attr *attr)
>>+{
>>+	int ret = 0;
>>+
>>+	if (dpll_attr_valid(DPLLA_MODE, attr))
>>+		ret = dpll_notify_device_change(dpll, DPLL_CHANGE_MODE, NULL);
>>+	if (!ret && dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr))
>>+		ret = dpll_notify_device_change(dpll, DPLL_CHANGE_SOURCE_PIN,
>>+						NULL);
>>+	return ret;
>>+}
>>+
>>+static struct pin_ref_dpll
>>+*dpll_pin_find_ref(struct dpll_device *dpll, struct dpll_pin *pin)
>>+{
>>+	struct pin_ref_dpll *ref;
>>+	unsigned long index;
>>+
>>+	xa_for_each(&pin->ref_dplls, index, ref) {
>>+		if (ref->dpll != dpll)
>>+			continue;
>>+		else
>>+			return ref;
>>+	}
>>+
>>+	return NULL;
>>+}
>>+
>>+static int
>>+dpll_pin_set_attr_single_ref(struct dpll_device *dpll, struct dpll_pin
>*pin,
>>+			     const struct dpll_pin_attr *attr)
>>+{
>>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	mutex_lock(&ref->dpll->lock);
>>+	ret = ref->ops->set(ref->dpll, pin, attr);
>>+	if (!ret)
>>+		dpll_notify_pin_change_attr(dpll, pin, attr);
>>+	mutex_unlock(&ref->dpll->lock);
>>+
>>+	return ret;
>>+}
>>+
>>+static int
>>+dpll_pin_set_attr_all_refs(struct dpll_pin *pin,
>>+			   const struct dpll_pin_attr *attr)
>>+{
>>+	struct pin_ref_dpll *ref;
>>+	unsigned long index;
>>+	int ret;
>>+
>>+	xa_for_each(&pin->ref_dplls, index, ref) {
>>+		if (!ref->dpll)
>>+			return -EFAULT;
>>+		if (!ref || !ref->ops || !ref->ops->set)
>>+			return -EOPNOTSUPP;
>>+		mutex_lock(&ref->dpll->lock);
>>+		ret = ref->ops->set(ref->dpll, pin, attr);
>>+		mutex_unlock(&ref->dpll->lock);
>>+		if (!ret)
>>+			dpll_notify_pin_change_attr(ref->dpll, pin, attr);
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+int dpll_pin_set_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      const struct dpll_pin_attr *attr)
>>+{
>>+	struct dpll_pin_attr *tmp_attr;
>>+	int ret;
>>+
>>+	tmp_attr = dpll_pin_attr_alloc();
>>+	if (!tmp_attr)
>>+		return -ENOMEM;
>>+	ret = dpll_pin_attr_prep_common(tmp_attr, attr);
>>+	if (ret < 0)
>>+		goto tmp_free;
>>+	if (ret == PIN_ATTR_CHANGE) {
>>+		ret = dpll_pin_set_attr_all_refs(pin, tmp_attr);
>>+		if (ret)
>>+			goto tmp_free;
>>+	}
>>+
>>+	ret = dpll_pin_attr_prep_exclusive(tmp_attr, attr);
>>+	if (ret < 0)
>>+		goto tmp_free;
>>+	if (ret == PIN_ATTR_CHANGE)
>>+		ret = dpll_pin_set_attr_single_ref(dpll, pin, tmp_attr);
>>+
>>+tmp_free:
>>+	dpll_pin_attr_free(tmp_attr);
>>+	return ret;
>>+}
>>+
>>+int dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      struct dpll_pin_attr *attr)
>>+{
>>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>>+	int ret;
>>+
>>+	if (!ref)
>>+		return -ENODEV;
>>+	if (!ref->ops || !ref->ops->get)
>>+		return -EOPNOTSUPP;
>>+
>>+	ret = ref->ops->get(dpll, pin, attr);
>>+	if (ret)
>>+		return -EAGAIN;
>>+
>>+	return ret;
>>+}
>>+
>>+const char *dpll_pin_get_description(struct dpll_pin *pin)
>>+{
>>+	return pin->description;
>>+}
>>+
>>+struct dpll_pin *dpll_pin_get_parent(struct dpll_pin *pin)
>>+{
>>+	return pin->parent_pin;
>>+}
>
>These helpers should not exist. Not needed for anything good.
>

Will be removed as no longer needed to find a pin "externally".

>
>
>>+
>>+int dpll_set_attr(struct dpll_device *dpll, const struct dpll_attr *attr)
>>+{
>>+	int ret;
>>+
>>+	if (dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr)) {
>>+		struct pin_ref_dpll *ref;
>>+		struct dpll_pin *pin;
>>+		u32 source_idx;
>>+
>>+		ret = dpll_attr_source_idx_get(attr, &source_idx);
>>+		if (ret)
>>+			return -EINVAL;
>>+		pin = dpll_pin_get_by_idx(dpll, source_idx);
>>+		if (!pin)
>>+			return -ENXIO;
>>+		ref = dpll_pin_find_ref(dpll, pin);
>>+		if (!ref || !ref->ops)
>>+			return -EFAULT;
>>+		if (!ref->ops->select)
>>+			return -ENODEV;
>>+		dpll_lock(ref->dpll);
>>+		ret = ref->ops->select(ref->dpll, pin);
>>+		dpll_unlock(ref->dpll);
>>+		if (ret)
>>+			return -EINVAL;
>>+		dpll_notify_device_change_attr(dpll, attr);
>>+	}
>>+
>>+	if (dpll_attr_valid(DPLLA_MODE, attr)) {
>>+		dpll_lock(dpll);
>>+		ret = dpll->ops->set(dpll, attr);
>>+		dpll_unlock(dpll);
>>+		if (ret)
>>+			return -EINVAL;
>>+	}
>>+	dpll_notify_device_change_attr(dpll, attr);
>>+
>>+	return ret;
>>+}
>>+
>>+int dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr)
>>+{
>>+	if (!dpll)
>>+		return -ENODEV;
>>+	if (!dpll->ops || !dpll->ops->get)
>>+		return -EOPNOTSUPP;
>>+	if (dpll->ops->get(dpll, attr))
>>+		return -EAGAIN;
>>+
>>+	return 0;
>>+}
>>+
>>+void dpll_lock(struct dpll_device *dpll)
>>+{
>>+	mutex_lock(&dpll->lock);
>>+}
>>+
>>+void dpll_unlock(struct dpll_device *dpll)
>>+{
>>+	mutex_unlock(&dpll->lock);
>>+}
>>+
>>+void *dpll_priv(struct dpll_device *dpll)
>>+{
>>+	return dpll->priv;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_priv);
>
>Why do you need this?
>

Called from registering module inside of callback functions to obtain correct
driver instance data.

>
>>+
>>+void *dpll_pin_priv(struct dpll_device *dpll, struct dpll_pin *pin)
>>+{
>>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>>+
>>+	if (!ref)
>>+		return NULL;
>>+
>>+	return ref->priv;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_priv);
>
>And this.
>

Same as above.

>
>>+
>>+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..9cefecdfc47b
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.h
>>@@ -0,0 +1,176 @@
>>+/* 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)
>>+
>>+#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))
>>+
>>+
>>+/**
>>+ * dpll_device_get_by_id - find dpll device by it's id
>>+ * @id: dpll id
>>+ *
>>+ * Return: dpll_device struct if found, NULL otherwise.
>>+ */
>
>Please move the functions comments into .c file.
>

OK, will do.

>
>>+struct dpll_device *dpll_device_get_by_id(int id);
>>+
>>+/**
>>+ * dpll_device_get_by_name - find dpll device by it's id
>>+ * @name: dpll name
>>+ *
>>+ * Return: dpll_device struct if found, NULL otherwise.
>>+ */
>>+struct dpll_device *dpll_device_get_by_name(const char *name);
>>+
>>+/**
>>+ * 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);
>>+
>>+/**
>>+ * 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);
>>+
>>+/**
>>+ * 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);
>>+
>>+/**
>>+ * 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);
>>+
>>+/**
>>+ * 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);
>>+
>>+/**
>>+ * dpll_id - return dpll id
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: dpll id.
>>+ */
>>+u32 dpll_id(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_pin_idx - return dpll name
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Return: dpll name.
>>+ */
>>+const char *dpll_dev_name(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_lock - locks the dpll using internal mutex
>>+ * @dpll: registered dpll pointer
>>+ */
>>+void dpll_lock(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_unlock - unlocks the dpll using internal mutex
>>+ * @dpll: registered dpll pointer
>>+ */
>>+void dpll_unlock(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_set_attr - handler for dpll subsystem: dpll set attributes
>>+ * @dpll: registered dpll pointer
>>+ * @attr: dpll attributes
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_set_attr(struct dpll_device *dpll, const struct dpll_attr
>*attr);
>>+
>>+/**
>>+ * dpll_get_attr - handler for dpll subsystem: dpll get attributes
>>+ * @dpll: registered dpll pointer
>>+ * @attr: dpll attributes
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr);
>>+
>>+/**
>>+ * dpll_pin_idx - return dpll id
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Return: dpll id.
>>+ */
>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_pin_get_attr - handler for dpll subsystem: dpll pin get
>attributes
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @attr: dpll pin attributes
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      struct dpll_pin_attr *attr);
>>+
>>+/**
>>+ * dpll_pin_get_description - provide pin's description string
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Return: pointer to a description string.
>>+ */
>>+const char *dpll_pin_get_description(struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_pin_get_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_get_parent(struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_pin_set_attr - handler for dpll subsystem: dpll pin get
>attributes
>>+ * @dpll: registered dpll pointer
>>+ * @pin: registered pin pointer
>>+ * @attr: dpll pin attributes
>>+ *
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_pin_set_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		      const struct dpll_pin_attr *attr);
>>+
>>+#endif
>>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>>new file mode 100644
>>index 000000000000..9a1f682a42ac
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.c
>>@@ -0,0 +1,963 @@
>>+// 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_DUMP_FILTER]	= { .type = NLA_U32 },
>>+	[DPLLA_NETIFINDEX]	= { .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_TYPE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>>+	[DPLLA_PIN_STATE]	= { .type = NLA_U32 },
>>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>>+};
>>+
>>+struct dpll_param {
>>+	struct netlink_callback *cb;
>>+	struct sk_buff *msg;
>>+	struct dpll_device *dpll;
>>+	struct dpll_pin *pin;
>>+	enum dpll_event_change change_type;
>>+};
>>+
>>+struct dpll_dump_ctx {
>>+	int dump_filter;
>>+};
>>+
>>+typedef int (*cb_t)(struct dpll_param *);
>>+
>>+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_attr
>*attr)
>>+{
>>+	enum dpll_mode m = dpll_attr_mode_get(attr);
>>+
>>+	if (m == DPLL_MODE_UNSPEC)
>>+		return 0;
>>+
>>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>>+}
>>+
>>+static int dpll_msg_add_modes_supported(struct sk_buff *msg,
>>+					const struct dpll_attr *attr)
>>+{
>>+	enum dpll_mode i;
>>+	int  ret = 0;
>>+
>>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>>+		if (dpll_attr_mode_supported(attr, i)) {
>>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>>+			if (ret)
>>+				return -EMSGSIZE;
>>+		}
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_attr
>*attr)
>>+{
>>+	u32 source_idx;
>>+
>>+	if (dpll_attr_source_idx_get(attr, &source_idx))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_netifindex(struct sk_buff *msg, struct dpll_attr
>*attr)
>>+{
>>+	unsigned int netifindex; // TODO: Should be u32?
>>+
>>+	if (dpll_attr_netifindex_get(attr, &netifindex))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_NETIFINDEX, netifindex))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_attr
>*attr)
>>+{
>>+	enum dpll_lock_status s = dpll_attr_lock_status_get(attr);
>>+
>>+	if (s == DPLL_LOCK_STATUS_UNSPEC)
>>+		return 0;
>>+	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_attr *attr)
>>+{
>>+	s32 temp;
>>+
>>+	if (dpll_attr_temp_get(attr, &temp))
>>+		return 0;
>>+	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, enum dplla attr,
>>+				   enum dpll_pin_type type)
>>+{
>>+	if (nla_put_s32(msg, attr, type))
>>+		return -EMSGSIZE;
>
>
>Please avoid these pointless helpers and call nla_put_*() directly.
>
>
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_pin_attr
>*attr)
>>+{
>>+	enum dpll_pin_type t = dpll_pin_attr_type_get(attr);
>>+
>>+	if (t == DPLL_PIN_TYPE_UNSPEC)
>>+		return 0;
>>+
>>+	return __dpll_msg_add_pin_type(msg, DPLLA_PIN_TYPE, t);
>>+}
>>+
>>+static int dpll_msg_add_pin_types_supported(struct sk_buff *msg,
>>+					    const struct dpll_pin_attr *attr)
>>+{
>>+	enum dpll_pin_type i;
>>+	int ret;
>>+
>>+	for (i = DPLL_PIN_TYPE_UNSPEC + 1; i <= DPLL_PIN_TYPE_MAX; i++) {
>>+		if (dpll_pin_attr_type_supported(attr, i)) {
>>+			ret = __dpll_msg_add_pin_type(msg,
>>+						      DPLLA_PIN_TYPE_SUPPORTED,
>>+						      i);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	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_pin_attr *attr)
>>+{
>>+	enum dpll_pin_signal_type t = dpll_pin_attr_signal_type_get(attr);
>>+
>>+	if (t == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>>+		return 0;
>>+
>>+	return __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>>+					const struct dpll_pin_attr *attr)
>>+{
>>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>>+	enum dpll_pin_signal_type i;
>>+	int ret;
>>+
>>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>>+		if (dpll_pin_attr_signal_type_supported(attr, i)) {
>>+			ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>>+			if (ret)
>>+				return ret;
>>+		}
>>+	}
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_custom_freq(struct sk_buff *msg,
>>+					const struct dpll_pin_attr *attr)
>>+{
>>+	u32 freq;
>>+
>>+	if (dpll_pin_attr_custom_freq_get(attr, &freq))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_states(struct sk_buff *msg,
>>+				   const struct dpll_pin_attr *attr)
>>+{
>>+	enum dpll_pin_state i;
>>+
>>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>>+		if (dpll_pin_attr_state_enabled(attr, i))
>>+			if (nla_put_s32(msg, DPLLA_PIN_STATE, i))
>>+				return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int dpll_msg_add_pin_states_supported(struct sk_buff *msg,
>>+					     const struct dpll_pin_attr *attr)
>>+{
>>+	enum dpll_pin_state i;
>>+
>>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>>+		if (dpll_pin_attr_state_supported(attr, i))
>>+			if (nla_put_s32(msg, DPLLA_PIN_STATE_SUPPORTED, i))
>>+				return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_pin_attr
>*attr)
>>+{
>>+	u32 prio;
>>+
>>+	if (dpll_pin_attr_prio_get(attr, &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_pin_attr *attr)
>>+{
>>+	unsigned int netifindex; // TODO: Should be u32?
>>+
>>+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
>>+		return 0;
>>+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_event_change_type(struct sk_buff *msg,
>>+			       enum dpll_event_change event)
>>+{
>>+	if (nla_put_s32(msg, DPLLA_CHANGE_TYPE, event))
>>+		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_attr *attr = dpll_pin_attr_alloc();
>>+	struct dpll_pin *parent = NULL;
>>+	int ret;
>>+
>>+	if (!attr)
>>+		return -ENOMEM;
>>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_description(msg,
>dpll_pin_get_description(pin));
>>+	if (ret)
>>+		goto out;
>>+	parent = dpll_pin_get_parent(pin);
>>+	if (parent) {
>>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>>+								    parent));
>>+		if (ret)
>>+			goto out;
>>+	}
>>+	ret = dpll_pin_get_attr(dpll, pin, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_type(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_types_supported(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_signal_type(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_signal_types_supported(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_custom_freq(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_states(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_states_supported(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_prio(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+	ret = dpll_msg_add_pin_netifindex(msg, attr);
>>+	if (ret)
>>+		goto out;
>>+out:
>>+	dpll_pin_attr_free(attr);
>>+
>>+	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)
>>+{
>>+	struct dpll_attr *attr = dpll_attr_alloc();
>>+	int ret = dpll_get_attr(dpll, attr);
>>+
>>+	if (ret)
>>+		return -EAGAIN;
>>+	if (dpll_msg_add_source_pin(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_temp(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_lock_status(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_mode(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_modes_supported(msg, attr))
>>+		return -EMSGSIZE;
>>+	if (dpll_msg_add_netifindex(msg, attr))
>>+		return -EMSGSIZE;
>>+
>>+	return 0;
>>+}
>>+
>>+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_DUMP_FILTER_STATUS) {
>>+		ret = __dpll_cmd_dump_status(msg, dpll);
>>+		if (ret)
>>+			goto out_unlock;
>>+	}
>>+	if (dump_filter & DPLL_DUMP_FILTER_PINS)
>>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>>+	dpll_unlock(dpll);
>>+
>>+	return ret;
>>+out_unlock:
>>+	dpll_unlock(dpll);
>>+	return ret;
>>+}
>>+
>>+static enum dpll_pin_type dpll_msg_read_pin_type(struct nlattr *a)
>>+{
>>+	return nla_get_s32(a);
>
>
>No need to have this pointless boilerplate helpers, just call nla_get_x()
>directly.
>
They read correct type from nlattr and return right type for each attribute,
thus all are not pointless.
But I will remove them as they are not reused anywhere.

>
>>+}
>>+
>>+static enum dpll_pin_signal_type dpll_msg_read_pin_sig_type(struct nlattr
>*a)
>>+{
>>+	return nla_get_s32(a);
>>+}
>>+
>>+static u32 dpll_msg_read_pin_custom_freq(struct nlattr *a)
>>+{
>>+	return nla_get_u32(a);
>>+}
>>+
>>+static enum dpll_pin_state dpll_msg_read_pin_state(struct nlattr *a)
>>+{
>>+	return nla_get_s32(a);
>>+}
>>+
>>+static u32 dpll_msg_read_pin_prio(struct nlattr *a)
>>+{
>>+	return nla_get_u32(a);
>>+}
>>+
>>+static u32 dpll_msg_read_dump_filter(struct nlattr *a)
>>+{
>>+	return nla_get_u32(a);
>>+}
>>+
>>+static int
>>+dpll_pin_attr_from_nlattr(struct dpll_pin_attr *pa, struct genl_info
>*info)
>>+{
>>+	enum dpll_pin_signal_type st;
>>+	enum dpll_pin_state state;
>>+	enum dpll_pin_type t;
>>+	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_TYPE:
>
>Does not make sense to me. Why do you allow to set the pin type? That
>should be something constant, according to the hardware architecture.
>

Changed as suggested, if multiple pin types are possible on the hardware, they
shall be handled with MUX-type pin and multiple pins connected to it.

>
>>+			t = dpll_msg_read_pin_type(a);
>>+			ret = dpll_pin_attr_type_set(pa, t);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_SIGNAL_TYPE:
>>+			st = dpll_msg_read_pin_sig_type(a);
>>+			ret = dpll_pin_attr_signal_type_set(pa, st);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_CUSTOM_FREQ:
>>+			freq = dpll_msg_read_pin_custom_freq(a);
>>+			ret = dpll_pin_attr_custom_freq_set(pa, freq);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_STATE:
>>+			state = dpll_msg_read_pin_state(a);
>>+			ret = dpll_pin_attr_state_set(pa, state);
>>+			if (ret)
>>+				return ret;
>>+			break;
>>+		case DPLLA_PIN_PRIO:
>>+			prio = dpll_msg_read_pin_prio(a);
>>+			ret = dpll_pin_attr_prio_set(pa, 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_pin_attr *old = NULL, *new = NULL, *delta = NULL;
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	struct nlattr **attrs = info->attrs;
>>+	struct dpll_pin *pin;
>>+	int ret, pin_id;
>>+
>>+	if (!attrs[DPLLA_PIN_IDX])
>>+		return -EINVAL;
>>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>>+	old = dpll_pin_attr_alloc();
>>+	new = dpll_pin_attr_alloc();
>>+	delta = dpll_pin_attr_alloc();
>>+	if (!old || !new || !delta) {
>>+		ret = -ENOMEM;
>>+		goto mem_free;
>>+	}
>>+	dpll_lock(dpll);
>>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>>+	if (!pin) {
>>+		ret = -ENODEV;
>>+		goto mem_free_unlock;
>>+	}
>>+	ret = dpll_pin_get_attr(dpll, pin, old);
>>+	if (ret)
>>+		goto mem_free_unlock;
>>+	ret = dpll_pin_attr_from_nlattr(new, info);
>>+	if (ret)
>>+		goto mem_free_unlock;
>>+	ret = dpll_pin_attr_delta(delta, new, old);
>>+	dpll_unlock(dpll);
>>+	if (!ret)
>>+		ret = dpll_pin_set_attr(dpll, pin, delta);
>>+	else
>>+		ret = -EINVAL;
>>+
>>+	dpll_pin_attr_free(delta);
>>+	dpll_pin_attr_free(new);
>>+	dpll_pin_attr_free(old);
>>+
>>+	return ret;
>>+
>>+mem_free_unlock:
>>+	dpll_unlock(dpll);
>>+mem_free:
>>+	dpll_pin_attr_free(delta);
>>+	dpll_pin_attr_free(new);
>>+	dpll_pin_attr_free(old);
>>+	return ret;
>>+}
>>+
>>+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_attr_from_nlattr(struct dpll_attr *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_attr_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_attr_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_attr *old = NULL, *new = NULL, *delta = NULL;
>>+	struct dpll_device *dpll = info->user_ptr[0];
>>+	int ret;
>>+
>>+	old = dpll_attr_alloc();
>>+	new = dpll_attr_alloc();
>>+	delta = dpll_attr_alloc();
>>+	if (!old || !new || !delta) {
>>+		ret = -ENOMEM;
>>+		goto mem_free;
>>+	}
>>+	dpll_lock(dpll);
>>+	ret = dpll_get_attr(dpll, old);
>>+	dpll_unlock(dpll);
>>+	if (!ret) {
>>+		dpll_attr_from_nlattr(new, info);
>>+		ret = dpll_attr_delta(delta, new, old);
>>+		if (!ret)
>>+			ret = dpll_set_attr(dpll, delta);
>>+	}
>>+
>>+mem_free:
>>+	dpll_attr_free(old);
>>+	dpll_attr_free(new);
>>+	dpll_attr_free(delta);
>>+
>>+	return ret;
>>+}
>>+
>>+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_DUMP_FILTER])
>
>It's not a "dump" filter for "get" callback. Btw, why do you need this
>filtering at all? Do you expect some significant amount of pins assigned
>to dpll so you need to filter them out? If not, leave the filtering
>mechanism out for now to make things easier.
>

It works for both, will change name to DPLLA_FILTER for less confusion.

IMHO most important is to be able to separate DPLL status from the pins,
dumping the pins can be intensive, but once everything is configured user
would most likely just need dpll status.

>
>
>>+		dump_filter =
>>+			dpll_msg_read_dump_filter(attrs[DPLLA_DUMP_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_DUMP_FILTER];
>>+
>>+	if (attr)
>>+		ctx->dump_filter = dpll_msg_read_dump_filter(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);
>
>You are missing some locking here. What is protecting the driver from
>unregistering the instance right at the time you have this
>pointer returned?
>
>You need some reference counting and locking scheme to be defined and
>used.

True, we need a fix here.

>
>
>>+		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]);
>
>I still think that we should have 1 handle for dpll.
>We don't need DPLLA_ID. It is not stable, not sure anyone will use it.
>Why don't you have bus/name handle tuple similar to how we do it in
>devlink? It proved to be working good over the years. Check out:
>DEVLINK_ATTR_BUS_NAME
>DEVLINK_ATTR_DEV_NAME
>

Well, don't have strong opinion here.

>
>>+
>>+		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 dpll_param *p)
>>+{
>>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_name(p->msg, dpll_dev_name(p->dpll));
>>+
>>+	return ret;
>>+}
>>+
>>+static int dpll_event_device_change(struct dpll_param *p)
>>+{
>>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>>+
>>+	if (ret)
>>+		return ret;
>>+	ret = dpll_msg_add_event_change_type(p->msg, p->change_type);
>>+	if (ret)
>>+		return ret;
>>+	switch (p->change_type)	{
>>+	case DPLL_CHANGE_PIN_ADD:
>>+	case DPLL_CHANGE_PIN_DEL:
>>+	case DPLL_CHANGE_PIN_TYPE:
>>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>>+	case DPLL_CHANGE_PIN_STATE:
>>+	case DPLL_CHANGE_PIN_PRIO:
>>+		ret = dpll_msg_add_pin_idx(p->msg, dpll_pin_idx(p->dpll, p-
>>pin));
>>+		break;
>>+	default:
>>+		break;
>>+	}
>>+
>>+	return ret;
>>+}
>>+
>>+static const cb_t event_cb[] = {
>>+	[DPLL_EVENT_DEVICE_CREATE]	= dpll_event_device_id,
>>+	[DPLL_EVENT_DEVICE_DELETE]	= dpll_event_device_id,
>>+	[DPLL_EVENT_DEVICE_CHANGE]	= dpll_event_device_change,
>>+};
>>+
>>+/*
>>+ * Generic netlink DPLL event encoding
>>+ */
>>+static int dpll_send_event(enum dpll_event event, struct dpll_param *p)
>>+{
>>+	struct sk_buff *msg;
>>+	int ret = -EMSGSIZE;
>>+	void *hdr;
>>+
>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+	if (!msg)
>>+		return -ENOMEM;
>>+	p->msg = msg;
>>+
>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>>+	if (!hdr)
>>+		goto out_free_msg;
>>+
>>+	ret = event_cb[event](p);
>>+	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)
>>+{
>>+	struct dpll_param p = { .dpll = dpll };
>>+
>>+	return dpll_send_event(DPLL_EVENT_DEVICE_CREATE, &p);
>>+}
>>+
>>+int dpll_notify_device_delete(struct dpll_device *dpll)
>>+{
>>+	struct dpll_param p = { .dpll = dpll };
>>+
>>+	return dpll_send_event(DPLL_EVENT_DEVICE_DELETE, &p);
>>+}
>>+
>>+int dpll_notify_device_change(struct dpll_device *dpll,
>>+			      enum dpll_event_change event,
>>+			      struct dpll_pin *pin)
>>+{
>>+	struct dpll_param p = { .dpll = dpll,
>>+				.change_type = event,
>>+				.pin = pin };
>>+
>>+	return dpll_send_event(DPLL_EVENT_DEVICE_CHANGE, &p);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_notify_device_change);
>>+
>>+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..a9cbf260624d
>>--- /dev/null
>>+++ b/include/linux/dpll.h
>>@@ -0,0 +1,261 @@
>>+/* 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/dpll_attr.h>
>>+#include <linux/device.h>
>>+
>>+struct dpll_device;
>>+struct dpll_pin;
>>+
>>+#define DPLL_COOKIE_LEN		10
>>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>
>Plese maintain the namespace prefix.
>

Sure.

>
>>+struct dpll_device_ops {
>>+	int (*get)(struct dpll_device *dpll, struct dpll_attr *attr);
>>+	int (*set)(struct dpll_device *dpll, const struct dpll_attr *attr);
>>+};
>>+
>>+struct dpll_pin_ops {
>>+	int (*get)(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		   struct dpll_pin_attr *attr);
>>+	int (*set)(struct dpll_device *dpll, struct dpll_pin *pin,
>>+		   const struct dpll_pin_attr *attr);
>>+	int (*select)(struct dpll_device *dpll, struct dpll_pin *pin);
>>+};
>>+
>>+enum dpll_type {
>>+	DPLL_TYPE_UNSPEC,
>
>You are not in UAPI, no need of unspec here.
>

Will move into uapi as this value would be part of generated dpll
device name, thus seems useful for userspace.

>
>>+	DPLL_TYPE_PPS,
>>+	DPLL_TYPE_EEC,
>
>What exactly are these? Some comment would be really good here.
>

Sure.

>
>
>>+
>>+	__DPLL_TYPE_MAX
>>+};
>>+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
>>+
>>+/**
>>+ * dpll_device_alloc - allocate memory for a new dpll_device object
>>+ * @ops: pointer to dpll operations structure
>>+ * @type: type of a dpll being allocated
>>+ * @cookie: a system unique number for a device
>>+ * @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: cookie, type and dev_driver_idx.
>>+ * Finding allocated and registered dpll device is also possible with
>>+ * the: cookie, 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 u8 cookie[DPLL_COOKIE_LEN], u8 dev_driver_idx,
>>+		   void *priv, struct device *parent);
>>+
>>+/**
>>+ * dpll_device_register - registers allocated dpll
>>+ * @dpll: pointer to dpll
>>+ *
>>+ * Register the dpll on the dpll subsystem, make it available for netlink
>>+ * API users.
>>+ */
>>+void dpll_device_register(struct dpll_device *dpll);
>>+
>>+/**
>>+ * 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(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(struct dpll_device *dpll, 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
>>+ * @pin_idx: index of a pin on dpll device (@dpll_pin_owner)
>>+ *	     that is being registered on 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, u32 pin_idx,
>>+			 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
>>+ * @desc_len: number of chars in description
>>+ *
>>+ * 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, size_t
>desc_len);
>>+
>>+
>>+/**
>>+ * 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: pointer to object to register pin with
>>+ * @pin: pointer to allocated pin object being deregistered from dpll
>>+ * @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,
>>+			    struct dpll_pin *parent_pin, struct dpll_pin *pin,
>>+			    struct dpll_pin_ops *ops, void *priv);
>>+/**
>>+ * dpll_device_get_by_cookie - find a dpll by its cookie
>>+ * @cookie: cookie of dpll to search for, 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_cookie(u8 cookie[DPLL_COOKIE_LEN],
>>+					      enum dpll_type type, u8 idx);
>>+
>>+/**
>>+ * dpll_pin_get_by_description - find a pin by its description
>>+ * @dpll: dpll device pointer
>>+ * @description: string description 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_description(struct dpll_device *dpll,
>>+					     const char *description);
>>+
>>+/**
>>+ * 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);
>>+
>>+int dpll_notify_device_change(struct dpll_device *dpll,
>>+			      enum dpll_event_change event,
>>+			      struct dpll_pin *pin);
>>+#endif
>>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>>new file mode 100644
>>index 000000000000..16278618d59d
>>--- /dev/null
>>+++ b/include/uapi/linux/dpll.h
>>@@ -0,0 +1,263 @@
>>+/* 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 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_DUMP_FILTER_PINS	1
>>+#define DPLL_DUMP_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_DUMP_FILTER - filter bitmask (int, sum of DPLL_DUMP_FILTER_*
>defines)
>>+ * @DPLLA_NETIFINDEX - related network interface index
>>+ * @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_TYPE_SUPPORTED - pin types supported (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_STATE - state of pin's capabilities (enum dpll_pin_state)
>>+ * @DPLLA_PIN_STATE_SUPPORTED - available pin's capabilities
>>+ *	(enum dpll_pin_state)
>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>+ *	(enum dpll_change_type)
>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>+ **/
>>+enum dplla {
>>+	DPLLA_UNSPEC,
>>+	DPLLA_ID,
>>+	DPLLA_NAME,
>>+	DPLLA_MODE,
>>+	DPLLA_MODE_SUPPORTED,
>>+	DPLLA_SOURCE_PIN_IDX,
>>+	DPLLA_LOCK_STATUS,
>>+	DPLLA_TEMP,
>>+	DPLLA_DUMP_FILTER,
>>+	DPLLA_NETIFINDEX,
>>+	DPLLA_PIN,
>>+	DPLLA_PIN_IDX,
>>+	DPLLA_PIN_DESCRIPTION,
>>+	DPLLA_PIN_TYPE,
>>+	DPLLA_PIN_TYPE_SUPPORTED,
>>+	DPLLA_PIN_SIGNAL_TYPE,
>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>+	DPLLA_PIN_CUSTOM_FREQ,
>>+	DPLLA_PIN_STATE,
>>+	DPLLA_PIN_STATE_SUPPORTED,
>>+	DPLLA_PIN_PRIO,
>>+	DPLLA_PIN_PARENT_IDX,
>>+	DPLLA_CHANGE_TYPE,
>>+	DPLLA_PIN_NETIFINDEX,
>>+	__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,
>
>Good.
>
>
>>+
>>+	__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
>
>As I wrote in the reply to the cover letter, I don't think we should
>have this.
>

For now leaving.

>
>>+ * @DPLL_PIN_TYPE_EXT - external source
>
>Why "source" ? It could be an output too.
>
>
>>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>
>I don't follow why this is a PIN. It is internal clock of DPLL. It
>should not be exposed as a pin.
>

May be a good point, I am leaving it but let Vadim or Jakub confirm it is
not needed for them.
In ice dpll doesn't have internal oscillator, as it is located externally to
dpll. But at the same time we don't expose this pin to the user.

>
>>+ * @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,
>
>1HZ
>
>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>
>10000000Hz

IMHO it is better now.

>
>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>
>XHz
>
>Why don't we have this at all? There could be just u64 ATTR that carries
>frequency in Hz.
>

May be a good point, but again let the other say the word.

>
>We are missing signal type for SYNCE_ETH_PORT pin type. Is it supposed
>to be DPLL_PIN_SIGNAL_TYPE_UNSPEC? I think it might be if we stick with
>having this enum.
>

I would say DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ, or we can add something new.

>
>>+
>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>+};
>>+
>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>+
>>+/* dpll_pin_state - available pin states
>>+ *
>>+ * @DPLL_PIN_STATE_UNSPEC - unspecified value
>>+ * @DPLL_PIN_STATE_CONNECTED - pin connected
>>+ * @DPLL_PIN_STATE_DISCONNECTED - pin disconnected
>>+ * @DPLL_PIN_STATE_SOURCE - pin used as an input pin
>>+ * @DPLL_PIN_STATE_OUTPUT - pin used as an output pin
>>+ **/
>>+enum dpll_pin_state {
>>+	DPLL_PIN_STATE_UNSPEC,
>>+	DPLL_PIN_STATE_CONNECTED,
>>+	DPLL_PIN_STATE_DISCONNECTED,
>>+	DPLL_PIN_STATE_SOURCE,
>>+	DPLL_PIN_STATE_OUTPUT,
>
>The pin can be "connected" and "source" at the same time. How do you
>expose that. I think we should remove "connected and just have:
>	DPLL_PIN_STATE_DISCONNECTED,
>	DPLL_PIN_STATE_SOURCE,
>	DPLL_PIN_STATE_OUTPUT,

Userspace will get stream of attributes with the same id:
   DPLLA_PIN_IDX=19
        DPLLA_PIN_DESCRIPTION=C827_0-RCLKA-3
        DPLLA_PIN_TYPE=3
        DPLLA_PIN_PARENT_IDX=2
        DPLLA_PIN_SIGNAL_TYPE=3
        DPLLA_PIN_SIGNAL_TYPE_SUPPORTED=3
        DPLLA_PIN_STATE=1
        DPLLA_PIN_STATE=3
        DPLLA_PIN_STATE_SUPPORTED=1
        DPLLA_PIN_STATE_SUPPORTED=2
        DPLLA_PIN_STATE_SUPPORTED=3

>
>"state" also sound odd here, as it is not really a state. It is a "mode"
>of a pin which is controlled by the user. Could you call it "MODE"?
>

Sure, seems legit. will try.

>
>
>>+
>>+	__DPLL_PIN_STATE_MAX,
>>+};
>>+
>>+#define DPLL_PIN_STATE_MAX (__DPLL_PIN_STATE_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_TYPE - pin type cahnged,
>>+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
>>+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
>>+ * @DPLL_CHANGE_PIN_STATE - 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_TYPE,
>>+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
>>+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
>>+	DPLL_CHANGE_PIN_STATE,
>>+	DPLL_CHANGE_PIN_PRIO,
>
>I think it is odd to have this. With every future added attribute, you
>are going to add a value here as well. Basically you have 1:1
>relationship.
>
>I have to say I didn't see this concept in any other netlink usecase.
>Did you?
>
>Why exactly do you need it? Userspace usually maintains the internal
>object instance anyway, it can compare incoming message with that.
>

Heven't looked for it.
If there is no other opinion we can remove most of them.

>
>
>>+
>>+	__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_FORCED - source can be only selected by sending a request
>to dpll
>
>I would probably go rather for "MODE_MANUAL". "forced" does not sound
>correct there.
>

Sure.

>
>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>dpll
>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>
>Well, when user sets this, he basically tells the device to freerun.
>What would be different between setting this and DPLL_MODE_FREERUN?
>

We took it from ZL80032 specification:
In freerun the dpll is not doing any synchronization with any reference inputs,
output signal is based on the device's system clock frequency offset only.
The freerun accuracy of the output clock is equal to the accuracy of the system
clock.

HOLDOVER (actually FORCED HOLDOVER) is possible when there was a valid signal
and after requesting this mode: references are ignored and the DPLL must go to
holdover (at the frequency offset of the most recent selected reference)

>
>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>
>I'm clueless what this is. Isn't this Freerun too?

NCO is a freerun but it possible to control frequency, user can request
different frequency than the one of the system clock.


Thanks,
Arkadiusz
>
>
>>+ **/
>>+enum dpll_mode {
>>+	DPLL_MODE_UNSPEC,
>>+	DPLL_MODE_FORCED,
>>+	DPLL_MODE_AUTOMATIC,
>>+	DPLL_MODE_HOLDOVER,
>>+	DPLL_MODE_FREERUN,
>>+	DPLL_MODE_NCO,
>>+
>>+	__DPLL_MODE_MAX,
>>+};
>>+
>>+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
>>+
>>+#endif /* _UAPI_LINUX_DPLL_H */
>>--
>>2.27.0
>>

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

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

Fri, Dec 23, 2022 at 05:45:03PM CET, arkadiusz.kubalewski@intel.com wrote:
>>-----Original Message-----
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, November 30, 2022 4:21 PM
>>
>>Tue, Nov 29, 2022 at 10:37:22PM CET, vfedorenko@novek.ru wrote:
>>>From: Vadim Fedorenko <vadfed@fb.com>
>>>
>>>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@fb.com>
>>>---
>>> MAINTAINERS                 |   8 +
>>> drivers/Kconfig             |   2 +
>>> drivers/Makefile            |   1 +
>>> drivers/dpll/Kconfig        |   7 +
>>> drivers/dpll/Makefile       |  11 +
>>> drivers/dpll/dpll_core.c    | 760 ++++++++++++++++++++++++++++
>>> drivers/dpll/dpll_core.h    | 176 +++++++
>>> drivers/dpll/dpll_netlink.c | 963 ++++++++++++++++++++++++++++++++++++
>>> drivers/dpll/dpll_netlink.h |  24 +
>>> include/linux/dpll.h        | 261 ++++++++++
>>> include/uapi/linux/dpll.h   | 263 ++++++++++
>>> 11 files changed, 2476 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 61fe86968111..79b76cca9620 100644
>>>--- a/MAINTAINERS
>>>+++ b/MAINTAINERS
>>>@@ -6343,6 +6343,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 19ee995bd0ae..a3e00294a995 100644
>>>--- a/drivers/Kconfig
>>>+++ b/drivers/Kconfig
>>>@@ -239,4 +239,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..3391deb08014
>>>--- /dev/null
>>>+++ b/drivers/dpll/Makefile
>>>@@ -0,0 +1,11 @@
>>>+# SPDX-License-Identifier: GPL-2.0
>>>+#
>>>+# Makefile for DPLL drivers.
>>>+#
>>>+
>>>+obj-$(CONFIG_DPLL)          += dpll_sys.o
>>>+dpll_sys-y                  += dpll_attr.o
>>>+dpll_sys-y                  += dpll_core.o
>>>+dpll_sys-y                  += dpll_netlink.o
>>>+dpll_sys-y                  += dpll_pin_attr.o
>>>+
>>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>>new file mode 100644
>>>index 000000000000..013ac4150583
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_core.c
>>>@@ -0,0 +1,760 @@
>>>+// 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[PIN_DESC_LEN];
>>>+};
>>>+
>>>+/**
>>>+ * 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
>>>+ * @cookie:	unique identifier (cookie) of a dpll
>>>+ * @dev_driver_idx: provided by driver for
>>>+ */
>>>+struct dpll_device {
>>
>>Just "dpll" please. Device somehow indicates this is managing device on
>>the bus. It is misleading.
>>
>
>Left as asked in follow up email.
>
>>
>>>+	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;
>>>+	u8 cookie[DPLL_COOKIE_LEN];
>>>+	u8 dev_driver_idx;
>>>+};
>>>+
>>>+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 pin_ref_dpll {
>>
>>Namespace
>>
>
>Gonna fix in next version.
>
>>
>>>+	struct dpll_device *dpll;
>>>+	struct dpll_pin_ops *ops;
>>>+	void *priv;
>>>+};
>>>+
>>>+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;
>>>+}
>>>+
>>>+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_cookie(u8 cookie[DPLL_COOKIE_LEN],
>>>+					      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 (!memcmp(dpll->cookie, cookie, DPLL_COOKIE_LEN)) {
>>>+			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_cookie);
>>>+
>>>+static void dpll_device_release(struct device *dev)
>>>+{
>>>+	struct dpll_device *dpll;
>>>+
>>>+	dpll = to_dpll_device(dev);
>>>+
>>>+	dpll_device_unregister(dpll);
>>>+	dpll_device_free(dpll);
>>>+}
>>>+
>>>+static struct class dpll_class = {
>>>+	.name = "dpll",
>>>+	.dev_release = dpll_device_release,
>>>+};
>>>+
>>>+static const char *dpll_type_name[__DPLL_TYPE_MAX] = {
>>>+	[DPLL_TYPE_UNSPEC] = "",
>>>+	[DPLL_TYPE_PPS] = "PPS",
>>>+	[DPLL_TYPE_EEC] = "EEC",
>>>+};
>>>+
>>>+static const char *dpll_type_str(enum dpll_type type)
>>>+{
>>>+	if (type >= DPLL_TYPE_UNSPEC && type <= DPLL_TYPE_MAX)
>>>+		return dpll_type_name[type];
>>>+	else
>>>+		return "";
>>>+}
>>>+
>>>+struct dpll_device
>>>+*dpll_device_alloc(struct dpll_device_ops *ops, enum dpll_type type,
>>>+		   const u8 cookie[DPLL_COOKIE_LEN], u8 idx,
>>
>>This cooking thing should be removed alongside with the getter. Driver
>>should not need it.
>>
>
>Gonna fix in next version. Will replace with clock_id.
>
>>
>>>+		   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 = idx;
>>>+	memcpy(dpll->cookie, cookie, sizeof(dpll->cookie));
>>>+
>>>+	mutex_lock(&dpll_device_xa_lock);
>>>+	ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll,
>>
>>You have idx and id? Why?
>>
>
>id is system id, idx is given by driver registering it, as for now one driver
>can allocate many dplls with the same type and clock_id.

That didn't help me. What's "system id"? What's the use case of "idx"
passed by the driver?


>
>>
>>>+		       xa_limit_16b, GFP_KERNEL);
>>>+	if (ret)
>>>+		goto error;
>>>+	dev_set_name(&dpll->dev, "dpll-%s-%s-%s%d",
>>dev_driver_string(parent),
>>>+		     dev_name(parent), type ? dpll_type_str(type) : "", idx);
>>
>>Odd. You encode parent device name into a name. What do we have device
>>hierarchy for? Also, why to encode "type_str"? Does no make sense to me.
>>
>
>Gonna fix in next version.
>
>>
>>>+	dpll->priv = priv;
>>>+	xa_init_flags(&dpll->pins, XA_FLAGS_ALLOC);
>>>+	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);
>>>+
>>>+void dpll_device_register(struct dpll_device *dpll)
>>>+{
>>>+	ASSERT_DPLL_NOT_REGISTERED(dpll);
>>>+
>>>+	mutex_lock(&dpll_device_xa_lock);
>>>+	xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>>+	mutex_unlock(&dpll_device_xa_lock);
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_device_register);
>>>+
>>>+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);
>>>+
>>>+u32 dpll_id(struct dpll_device *dpll)
>>>+{
>>>+	return dpll->id;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_id);
>>>+
>>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin)
>>
>>This is odd. You just need pin here, no need to pass dpll.
>>
>
>It is needed for "dpll_pin_ref dance".

:(


>
>>
>>>+{
>>>+	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);
>>
>>General comment to this getter helpers. Why do you need them? Why the
>>driver cares about name, abou idx and other attributes. Why driver needs
>>to iterate over pins?
>>This should not be needed, please remove.
>>
>
>All is required to share a dplls and pins.

I don't see why that is the reason. Why would anyone (in kernel)
care about the "name" for example. If there is someone like that, smell
like clear misdesign.

Btw, didn't you find a way how to avoid this odd concept of pin sharing
for your device? I still believe that if we model things right, have a
"synchonizer" entity we don't need the sharing at all.


>Separated driver instances shall be able to find dpll or pin of the other
>instance.
>
>>
>>
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_dev_name);
>>>+
>>>+struct dpll_pin *dpll_pin_alloc(const char *description, size_t desc_len)
>>
>>
>>Why do you need description? In your pps example, you pass "SMA". Isn't
>>that rather a pin type? Const attribute of a pin?
>>I can understand a need for "label", if you have 2 SMA connectors
>>labeled "port 1" and "port 2", pass that.
>
>What about U.FL? We probably could add signal types like:
>- DPLL_PIN_SIGNAL_TYPE_EXT_SMA,
>- DPLL_PIN_SIGNAL_TYPE_EXT_UFL.
>Maybe more if there are some other viable connectors..
>Then pass label/description as you suggested.

Yes please.


>
>>
>>Also, it's a string, you don't need len.
>>Also, you can have this as vararg to make caller's live easier.
>>
>
>Sure, we are rather safe to remove desc_len, but I don't get how vararg would
>be beneficial here?

The caller might find it useful to include indexes for example, without
having and extra string buffer to print it to.


>
>>
>>
>>>+{
>>>+	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
>>>+
>>>+	if (!pin)
>>>+		return ERR_PTR(-ENOMEM);
>>>+	if (desc_len > PIN_DESC_LEN)
>>>+		return ERR_PTR(-EINVAL);
>>>+
>>>+	strncpy(pin->description, description, PIN_DESC_LEN);
>>>+	if (desc_len == PIN_DESC_LEN)
>>>+		pin->description[PIN_DESC_LEN - 1] = '\0';
>>>+	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
>>>+
>>>+	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, 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 pin_ref_dpll_add(struct dpll_pin *pin, struct dpll_device
>>*dpll,
>>>+			    struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	struct pin_ref_dpll *ref, *pos;
>>>+	unsigned long index;
>>>+	u32 idx;
>>>+
>>>+	ref = kzalloc(sizeof(struct pin_ref_dpll), 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 pin_ref_dpll_del(struct dpll_pin *pin, struct dpll_device
>>*dpll)
>>>+{
>>>+	struct pin_ref_dpll *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;
>>>+		}
>>>+	}
>>>+}
>>
>>As I wrote in the reply to the cover, I believe the sharing of pins
>>between instances is wrong, then you can avoid this ref dance.
>>
>
>I see this differently, as sharing pins and dplls is beneficial for complex
>hardware designs, leaving for now.


Could you please draw an example of this "complex hardware design" here?
I'm quite sure that you are trying to cut corners here introducing very
weird model to overcome some exising misdesign. I would like you
to prove me wrong!



>
>>
>>
>>>+
>>>+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 = pin_ref_dpll_add(pin, dpll, ops, priv);
>>>+		if (ret)
>>>+			pin_deregister_from_xa(&dpll->pins, pin);
>>>+	}
>>>+	mutex_unlock(&dpll->lock);
>>>+	if (!ret)
>>>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_ADD, pin);
>>>+
>>>+	return ret;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>>+
>>>+int
>>>+dpll_shared_pin_register(struct dpll_device *dpll_pin_owner,
>>>+			 struct dpll_device *dpll, u32 pin_idx,
>>>+			 struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	struct dpll_pin *pin;
>>>+	int ret;
>>>+
>>>+	mutex_lock(&dpll_pin_owner->lock);
>>>+	pin = dpll_pin_get_by_idx(dpll_pin_owner, pin_idx);
>>>+	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)
>>>+		pin_ref_dpll_del(pin, dpll);
>>>+	mutex_unlock(&dpll->lock);
>>>+	if (!ret)
>>>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_DEL, pin);
>>>+
>>>+	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,
>>>+			    struct dpll_pin *parent_pin, struct dpll_pin *pin,
>>>+			    struct dpll_pin_ops *ops, void *priv)
>>>+{
>>>+	int ret;
>>>+
>>>+	if (!parent_pin || !pin)
>>>+		return -EINVAL;
>>>+
>>>+	mutex_lock(&dpll->lock);
>>>+	ret = dpll_alloc_pin_on_xa(&dpll->pins, pin);
>>>+	if (!ret)
>>>+		ret = pin_ref_dpll_add(pin, dpll, ops, priv);
>>>+	if (!ret)
>>>+		pin->parent_pin = parent_pin;
>>>+	mutex_unlock(&dpll->lock);
>>>+	if (!ret)
>>>+		dpll_notify_device_change(dpll, DPLL_CHANGE_PIN_ADD, pin);
>>>+
>>>+	return ret;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_muxed_pin_register);
>>>+
>>>+struct dpll_pin
>>>+*dpll_pin_get_by_description(struct dpll_device *dpll, const char
>>*description)
>>
>>Why on earth would driver need this helper? If it does, something is
>>wrong. Please remove.
>>
>
>I.e. The driver wants to add it's recovered clock as a source for the MUX type
>pin. It finds dpll, finds a MUX type pin, allocates new pin, registers it with
>previously found MUX type pin.
>
>Searching for DPLL will be done now with a clock_id (previously cookie), type
>of dpll and index (all provided by driver on dpll device allocation).
>
>For searching of a pin, I think I am going to remove it, instead will modify:
>int dpll_muxed_pin_register(struct dpll_device *dpll,
>                            struct dpll_pin *parent_pin, struct dpll_pin *pin,
>                            struct dpll_pin_ops *ops, void *priv)
>Letting the user register with MUX-type pin label, instead of its pointer.
>This way searching of the pin won't be needed anymore.
>
>>
>>>+{
>>>+	struct dpll_pin *pos, *pin = NULL;
>>>+	unsigned long index;
>>>+
>>>+	mutex_lock(&dpll->lock);
>>>+	xa_for_each(&dpll->pins, index, pos) {
>>>+		if (!strncmp(pos->description, description, PIN_DESC_LEN)) {
>>>+			pin = pos;
>>>+			break;
>>>+		}
>>>+	}
>>>+	mutex_unlock(&dpll->lock);
>>>+
>>>+	return pin;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_pin_get_by_description);
>>>+
>>>+static 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;
>>>+}
>>>+
>>>+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);
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_pin_get_by_idx);
>>
>>Again, please remove these heplers. Driver should be happy only with
>>opaque struct dpll and struct dpll_pin
>>
>
>This won't be exported anymore.
>
>>
>>>+
>>>+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);
>>>+}
>>>+
>>>+struct dpll_pin *dpll_pin_next(struct dpll_device *dpll, unsigned long
>>*index)
>>>+{
>>>+	return xa_find_after(&dpll->pins, index, LONG_MAX, PIN_REGISTERED);
>>>+}
>>>+
>>>+struct dpll_device *dpll_first(unsigned long *index)
>>>+{
>>>+	*index = 0;
>>>+
>>>+	return xa_find(&dpll_device_xa, index, LONG_MAX, DPLL_REGISTERED);
>>>+}
>>>+
>>>+struct dpll_device *dpll_next(unsigned long *index)
>>>+{
>>>+	return xa_find_after(&dpll_device_xa, index, LONG_MAX,
>>DPLL_REGISTERED);
>>>+}
>>>+
>>>+static int
>>>+dpll_notify_pin_change_attr(struct dpll_device *dpll, struct dpll_pin
>>*pin,
>>>+			    const struct dpll_pin_attr *attr)
>>>+{
>>>+	enum dpll_event_change change;
>>>+	int ret = 0;
>>>+
>>>+	if (dpll_pin_attr_valid(DPLLA_PIN_TYPE, attr)) {
>>>+		change = DPLL_CHANGE_PIN_TYPE;
>>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>>+	}
>>>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_SIGNAL_TYPE, attr)) {
>>>+		change = DPLL_CHANGE_PIN_SIGNAL_TYPE;
>>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>>+	}
>>>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_CUSTOM_FREQ, attr)) {
>>>+		change = DPLL_CHANGE_PIN_CUSTOM_FREQ;
>>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>>+	}
>>>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_STATE, attr)) {
>>>+		change = DPLL_CHANGE_PIN_STATE;
>>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>>+	}
>>>+	if (!ret && dpll_pin_attr_valid(DPLLA_PIN_PRIO, attr)) {
>>>+		change = DPLL_CHANGE_PIN_PRIO;
>>>+		ret = dpll_notify_device_change(dpll, change, pin);
>>>+	}
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+static int dpll_notify_device_change_attr(struct dpll_device *dpll,
>>>+					  const struct dpll_attr *attr)
>>>+{
>>>+	int ret = 0;
>>>+
>>>+	if (dpll_attr_valid(DPLLA_MODE, attr))
>>>+		ret = dpll_notify_device_change(dpll, DPLL_CHANGE_MODE, NULL);
>>>+	if (!ret && dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr))
>>>+		ret = dpll_notify_device_change(dpll, DPLL_CHANGE_SOURCE_PIN,
>>>+						NULL);
>>>+	return ret;
>>>+}
>>>+
>>>+static struct pin_ref_dpll
>>>+*dpll_pin_find_ref(struct dpll_device *dpll, struct dpll_pin *pin)
>>>+{
>>>+	struct pin_ref_dpll *ref;
>>>+	unsigned long index;
>>>+
>>>+	xa_for_each(&pin->ref_dplls, index, ref) {
>>>+		if (ref->dpll != dpll)
>>>+			continue;
>>>+		else
>>>+			return ref;
>>>+	}
>>>+
>>>+	return NULL;
>>>+}
>>>+
>>>+static int
>>>+dpll_pin_set_attr_single_ref(struct dpll_device *dpll, struct dpll_pin
>>*pin,
>>>+			     const struct dpll_pin_attr *attr)
>>>+{
>>>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>>>+	int ret;
>>>+
>>>+	mutex_lock(&ref->dpll->lock);
>>>+	ret = ref->ops->set(ref->dpll, pin, attr);
>>>+	if (!ret)
>>>+		dpll_notify_pin_change_attr(dpll, pin, attr);
>>>+	mutex_unlock(&ref->dpll->lock);
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+static int
>>>+dpll_pin_set_attr_all_refs(struct dpll_pin *pin,
>>>+			   const struct dpll_pin_attr *attr)
>>>+{
>>>+	struct pin_ref_dpll *ref;
>>>+	unsigned long index;
>>>+	int ret;
>>>+
>>>+	xa_for_each(&pin->ref_dplls, index, ref) {
>>>+		if (!ref->dpll)
>>>+			return -EFAULT;
>>>+		if (!ref || !ref->ops || !ref->ops->set)
>>>+			return -EOPNOTSUPP;
>>>+		mutex_lock(&ref->dpll->lock);
>>>+		ret = ref->ops->set(ref->dpll, pin, attr);
>>>+		mutex_unlock(&ref->dpll->lock);
>>>+		if (!ret)
>>>+			dpll_notify_pin_change_attr(ref->dpll, pin, attr);
>>>+	}
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+int dpll_pin_set_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>>>+		      const struct dpll_pin_attr *attr)
>>>+{
>>>+	struct dpll_pin_attr *tmp_attr;
>>>+	int ret;
>>>+
>>>+	tmp_attr = dpll_pin_attr_alloc();
>>>+	if (!tmp_attr)
>>>+		return -ENOMEM;
>>>+	ret = dpll_pin_attr_prep_common(tmp_attr, attr);
>>>+	if (ret < 0)
>>>+		goto tmp_free;
>>>+	if (ret == PIN_ATTR_CHANGE) {
>>>+		ret = dpll_pin_set_attr_all_refs(pin, tmp_attr);
>>>+		if (ret)
>>>+			goto tmp_free;
>>>+	}
>>>+
>>>+	ret = dpll_pin_attr_prep_exclusive(tmp_attr, attr);
>>>+	if (ret < 0)
>>>+		goto tmp_free;
>>>+	if (ret == PIN_ATTR_CHANGE)
>>>+		ret = dpll_pin_set_attr_single_ref(dpll, pin, tmp_attr);
>>>+
>>>+tmp_free:
>>>+	dpll_pin_attr_free(tmp_attr);
>>>+	return ret;
>>>+}
>>>+
>>>+int dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>>>+		      struct dpll_pin_attr *attr)
>>>+{
>>>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>>>+	int ret;
>>>+
>>>+	if (!ref)
>>>+		return -ENODEV;
>>>+	if (!ref->ops || !ref->ops->get)
>>>+		return -EOPNOTSUPP;
>>>+
>>>+	ret = ref->ops->get(dpll, pin, attr);
>>>+	if (ret)
>>>+		return -EAGAIN;
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+const char *dpll_pin_get_description(struct dpll_pin *pin)
>>>+{
>>>+	return pin->description;
>>>+}
>>>+
>>>+struct dpll_pin *dpll_pin_get_parent(struct dpll_pin *pin)
>>>+{
>>>+	return pin->parent_pin;
>>>+}
>>
>>These helpers should not exist. Not needed for anything good.
>>
>
>Will be removed as no longer needed to find a pin "externally".
>
>>
>>
>>>+
>>>+int dpll_set_attr(struct dpll_device *dpll, const struct dpll_attr *attr)
>>>+{
>>>+	int ret;
>>>+
>>>+	if (dpll_attr_valid(DPLLA_SOURCE_PIN_IDX, attr)) {
>>>+		struct pin_ref_dpll *ref;
>>>+		struct dpll_pin *pin;
>>>+		u32 source_idx;
>>>+
>>>+		ret = dpll_attr_source_idx_get(attr, &source_idx);
>>>+		if (ret)
>>>+			return -EINVAL;
>>>+		pin = dpll_pin_get_by_idx(dpll, source_idx);
>>>+		if (!pin)
>>>+			return -ENXIO;
>>>+		ref = dpll_pin_find_ref(dpll, pin);
>>>+		if (!ref || !ref->ops)
>>>+			return -EFAULT;
>>>+		if (!ref->ops->select)
>>>+			return -ENODEV;
>>>+		dpll_lock(ref->dpll);
>>>+		ret = ref->ops->select(ref->dpll, pin);
>>>+		dpll_unlock(ref->dpll);
>>>+		if (ret)
>>>+			return -EINVAL;
>>>+		dpll_notify_device_change_attr(dpll, attr);
>>>+	}
>>>+
>>>+	if (dpll_attr_valid(DPLLA_MODE, attr)) {
>>>+		dpll_lock(dpll);
>>>+		ret = dpll->ops->set(dpll, attr);
>>>+		dpll_unlock(dpll);
>>>+		if (ret)
>>>+			return -EINVAL;
>>>+	}
>>>+	dpll_notify_device_change_attr(dpll, attr);
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+int dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr)
>>>+{
>>>+	if (!dpll)
>>>+		return -ENODEV;
>>>+	if (!dpll->ops || !dpll->ops->get)
>>>+		return -EOPNOTSUPP;
>>>+	if (dpll->ops->get(dpll, attr))
>>>+		return -EAGAIN;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+void dpll_lock(struct dpll_device *dpll)
>>>+{
>>>+	mutex_lock(&dpll->lock);
>>>+}
>>>+
>>>+void dpll_unlock(struct dpll_device *dpll)
>>>+{
>>>+	mutex_unlock(&dpll->lock);
>>>+}
>>>+
>>>+void *dpll_priv(struct dpll_device *dpll)
>>>+{
>>>+	return dpll->priv;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_priv);
>>
>>Why do you need this?
>>
>
>Called from registering module inside of callback functions to obtain correct
>driver instance data.

Can't you pass the priv directly instead? Why does the callback need
dpll pointer?


>
>>
>>>+
>>>+void *dpll_pin_priv(struct dpll_device *dpll, struct dpll_pin *pin)
>>>+{
>>>+	struct pin_ref_dpll *ref = dpll_pin_find_ref(dpll, pin);
>>>+
>>>+	if (!ref)
>>>+		return NULL;
>>>+
>>>+	return ref->priv;
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_pin_priv);
>>
>>And this.
>>
>
>Same as above.
>
>>
>>>+
>>>+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..9cefecdfc47b
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_core.h
>>>@@ -0,0 +1,176 @@
>>>+/* 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)
>>>+
>>>+#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))
>>>+
>>>+
>>>+/**
>>>+ * dpll_device_get_by_id - find dpll device by it's id
>>>+ * @id: dpll id
>>>+ *
>>>+ * Return: dpll_device struct if found, NULL otherwise.
>>>+ */
>>
>>Please move the functions comments into .c file.
>>
>
>OK, will do.
>
>>
>>>+struct dpll_device *dpll_device_get_by_id(int id);
>>>+
>>>+/**
>>>+ * dpll_device_get_by_name - find dpll device by it's id
>>>+ * @name: dpll name
>>>+ *
>>>+ * Return: dpll_device struct if found, NULL otherwise.
>>>+ */
>>>+struct dpll_device *dpll_device_get_by_name(const char *name);
>>>+
>>>+/**
>>>+ * 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);
>>>+
>>>+/**
>>>+ * 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);
>>>+
>>>+/**
>>>+ * 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);
>>>+
>>>+/**
>>>+ * 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);
>>>+
>>>+/**
>>>+ * 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);
>>>+
>>>+/**
>>>+ * dpll_id - return dpll id
>>>+ * @dpll: registered dpll pointer
>>>+ *
>>>+ * Return: dpll id.
>>>+ */
>>>+u32 dpll_id(struct dpll_device *dpll);
>>>+
>>>+/**
>>>+ * dpll_pin_idx - return dpll name
>>>+ * @dpll: registered dpll pointer
>>>+ *
>>>+ * Return: dpll name.
>>>+ */
>>>+const char *dpll_dev_name(struct dpll_device *dpll);
>>>+
>>>+/**
>>>+ * dpll_lock - locks the dpll using internal mutex
>>>+ * @dpll: registered dpll pointer
>>>+ */
>>>+void dpll_lock(struct dpll_device *dpll);
>>>+
>>>+/**
>>>+ * dpll_unlock - unlocks the dpll using internal mutex
>>>+ * @dpll: registered dpll pointer
>>>+ */
>>>+void dpll_unlock(struct dpll_device *dpll);
>>>+
>>>+/**
>>>+ * dpll_set_attr - handler for dpll subsystem: dpll set attributes
>>>+ * @dpll: registered dpll pointer
>>>+ * @attr: dpll attributes
>>>+ *
>>>+ * Return: 0 if succeeds, error code otherwise.
>>>+ */
>>>+int dpll_set_attr(struct dpll_device *dpll, const struct dpll_attr
>>*attr);
>>>+
>>>+/**
>>>+ * dpll_get_attr - handler for dpll subsystem: dpll get attributes
>>>+ * @dpll: registered dpll pointer
>>>+ * @attr: dpll attributes
>>>+ *
>>>+ * Return: 0 if succeeds, error code otherwise.
>>>+ */
>>>+int dpll_get_attr(struct dpll_device *dpll, struct dpll_attr *attr);
>>>+
>>>+/**
>>>+ * dpll_pin_idx - return dpll id
>>>+ * @dpll: registered dpll pointer
>>>+ * @pin: registered pin pointer
>>>+ *
>>>+ * Return: dpll id.
>>>+ */
>>>+u32 dpll_pin_idx(struct dpll_device *dpll, struct dpll_pin *pin);
>>>+
>>>+/**
>>>+ * dpll_pin_get_attr - handler for dpll subsystem: dpll pin get
>>attributes
>>>+ * @dpll: registered dpll pointer
>>>+ * @pin: registered pin pointer
>>>+ * @attr: dpll pin attributes
>>>+ *
>>>+ * Return: 0 if succeeds, error code otherwise.
>>>+ */
>>>+int dpll_pin_get_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>>>+		      struct dpll_pin_attr *attr);
>>>+
>>>+/**
>>>+ * dpll_pin_get_description - provide pin's description string
>>>+ * @pin: registered pin pointer
>>>+ *
>>>+ * Return: pointer to a description string.
>>>+ */
>>>+const char *dpll_pin_get_description(struct dpll_pin *pin);
>>>+
>>>+/**
>>>+ * dpll_pin_get_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_get_parent(struct dpll_pin *pin);
>>>+
>>>+/**
>>>+ * dpll_pin_set_attr - handler for dpll subsystem: dpll pin get
>>attributes
>>>+ * @dpll: registered dpll pointer
>>>+ * @pin: registered pin pointer
>>>+ * @attr: dpll pin attributes
>>>+ *
>>>+ * Return: 0 if succeeds, error code otherwise.
>>>+ */
>>>+int dpll_pin_set_attr(struct dpll_device *dpll, struct dpll_pin *pin,
>>>+		      const struct dpll_pin_attr *attr);
>>>+
>>>+#endif
>>>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>>>new file mode 100644
>>>index 000000000000..9a1f682a42ac
>>>--- /dev/null
>>>+++ b/drivers/dpll/dpll_netlink.c
>>>@@ -0,0 +1,963 @@
>>>+// 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_DUMP_FILTER]	= { .type = NLA_U32 },
>>>+	[DPLLA_NETIFINDEX]	= { .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_TYPE]	= { .type = NLA_U32 },
>>>+	[DPLLA_PIN_SIGNAL_TYPE]	= { .type = NLA_U32 },
>>>+	[DPLLA_PIN_CUSTOM_FREQ] = { .type = NLA_U32 },
>>>+	[DPLLA_PIN_STATE]	= { .type = NLA_U32 },
>>>+	[DPLLA_PIN_PRIO]	= { .type = NLA_U32 },
>>>+};
>>>+
>>>+struct dpll_param {
>>>+	struct netlink_callback *cb;
>>>+	struct sk_buff *msg;
>>>+	struct dpll_device *dpll;
>>>+	struct dpll_pin *pin;
>>>+	enum dpll_event_change change_type;
>>>+};
>>>+
>>>+struct dpll_dump_ctx {
>>>+	int dump_filter;
>>>+};
>>>+
>>>+typedef int (*cb_t)(struct dpll_param *);
>>>+
>>>+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_attr
>>*attr)
>>>+{
>>>+	enum dpll_mode m = dpll_attr_mode_get(attr);
>>>+
>>>+	if (m == DPLL_MODE_UNSPEC)
>>>+		return 0;
>>>+
>>>+	return __dpll_msg_add_mode(msg, DPLLA_MODE, m);
>>>+}
>>>+
>>>+static int dpll_msg_add_modes_supported(struct sk_buff *msg,
>>>+					const struct dpll_attr *attr)
>>>+{
>>>+	enum dpll_mode i;
>>>+	int  ret = 0;
>>>+
>>>+	for (i = DPLL_MODE_UNSPEC + 1; i <= DPLL_MODE_MAX; i++) {
>>>+		if (dpll_attr_mode_supported(attr, i)) {
>>>+			ret = __dpll_msg_add_mode(msg, DPLLA_MODE_SUPPORTED, i);
>>>+			if (ret)
>>>+				return -EMSGSIZE;
>>>+		}
>>>+	}
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+static int dpll_msg_add_source_pin(struct sk_buff *msg, struct dpll_attr
>>*attr)
>>>+{
>>>+	u32 source_idx;
>>>+
>>>+	if (dpll_attr_source_idx_get(attr, &source_idx))
>>>+		return 0;
>>>+	if (nla_put_u32(msg, DPLLA_SOURCE_PIN_IDX, source_idx))
>>>+		return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int dpll_msg_add_netifindex(struct sk_buff *msg, struct dpll_attr
>>*attr)
>>>+{
>>>+	unsigned int netifindex; // TODO: Should be u32?
>>>+
>>>+	if (dpll_attr_netifindex_get(attr, &netifindex))
>>>+		return 0;
>>>+	if (nla_put_u32(msg, DPLLA_NETIFINDEX, netifindex))
>>>+		return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_attr
>>*attr)
>>>+{
>>>+	enum dpll_lock_status s = dpll_attr_lock_status_get(attr);
>>>+
>>>+	if (s == DPLL_LOCK_STATUS_UNSPEC)
>>>+		return 0;
>>>+	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_attr *attr)
>>>+{
>>>+	s32 temp;
>>>+
>>>+	if (dpll_attr_temp_get(attr, &temp))
>>>+		return 0;
>>>+	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, enum dplla attr,
>>>+				   enum dpll_pin_type type)
>>>+{
>>>+	if (nla_put_s32(msg, attr, type))
>>>+		return -EMSGSIZE;
>>
>>
>>Please avoid these pointless helpers and call nla_put_*() directly.
>>
>>
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int
>>>+dpll_msg_add_pin_type(struct sk_buff *msg, const struct dpll_pin_attr
>>*attr)
>>>+{
>>>+	enum dpll_pin_type t = dpll_pin_attr_type_get(attr);
>>>+
>>>+	if (t == DPLL_PIN_TYPE_UNSPEC)
>>>+		return 0;
>>>+
>>>+	return __dpll_msg_add_pin_type(msg, DPLLA_PIN_TYPE, t);
>>>+}
>>>+
>>>+static int dpll_msg_add_pin_types_supported(struct sk_buff *msg,
>>>+					    const struct dpll_pin_attr *attr)
>>>+{
>>>+	enum dpll_pin_type i;
>>>+	int ret;
>>>+
>>>+	for (i = DPLL_PIN_TYPE_UNSPEC + 1; i <= DPLL_PIN_TYPE_MAX; i++) {
>>>+		if (dpll_pin_attr_type_supported(attr, i)) {
>>>+			ret = __dpll_msg_add_pin_type(msg,
>>>+						      DPLLA_PIN_TYPE_SUPPORTED,
>>>+						      i);
>>>+			if (ret)
>>>+				return ret;
>>>+		}
>>>+	}
>>>+
>>>+	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_pin_attr *attr)
>>>+{
>>>+	enum dpll_pin_signal_type t = dpll_pin_attr_signal_type_get(attr);
>>>+
>>>+	if (t == DPLL_PIN_SIGNAL_TYPE_UNSPEC)
>>>+		return 0;
>>>+
>>>+	return __dpll_msg_add_pin_signal_type(msg, DPLLA_PIN_SIGNAL_TYPE, t);
>>>+}
>>>+
>>>+static int
>>>+dpll_msg_add_pin_signal_types_supported(struct sk_buff *msg,
>>>+					const struct dpll_pin_attr *attr)
>>>+{
>>>+	const enum dplla da = DPLLA_PIN_SIGNAL_TYPE_SUPPORTED;
>>>+	enum dpll_pin_signal_type i;
>>>+	int ret;
>>>+
>>>+	for (i = DPLL_PIN_SIGNAL_TYPE_UNSPEC + 1;
>>>+	     i <= DPLL_PIN_SIGNAL_TYPE_MAX; i++) {
>>>+		if (dpll_pin_attr_signal_type_supported(attr, i)) {
>>>+			ret = __dpll_msg_add_pin_signal_type(msg, da, i);
>>>+			if (ret)
>>>+				return ret;
>>>+		}
>>>+	}
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int dpll_msg_add_pin_custom_freq(struct sk_buff *msg,
>>>+					const struct dpll_pin_attr *attr)
>>>+{
>>>+	u32 freq;
>>>+
>>>+	if (dpll_pin_attr_custom_freq_get(attr, &freq))
>>>+		return 0;
>>>+	if (nla_put_u32(msg, DPLLA_PIN_CUSTOM_FREQ, freq))
>>>+		return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int dpll_msg_add_pin_states(struct sk_buff *msg,
>>>+				   const struct dpll_pin_attr *attr)
>>>+{
>>>+	enum dpll_pin_state i;
>>>+
>>>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>>>+		if (dpll_pin_attr_state_enabled(attr, i))
>>>+			if (nla_put_s32(msg, DPLLA_PIN_STATE, i))
>>>+				return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int dpll_msg_add_pin_states_supported(struct sk_buff *msg,
>>>+					     const struct dpll_pin_attr *attr)
>>>+{
>>>+	enum dpll_pin_state i;
>>>+
>>>+	for (i = DPLL_PIN_STATE_UNSPEC + 1; i <= DPLL_PIN_STATE_MAX; i++)
>>>+		if (dpll_pin_attr_state_supported(attr, i))
>>>+			if (nla_put_s32(msg, DPLLA_PIN_STATE_SUPPORTED, i))
>>>+				return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int
>>>+dpll_msg_add_pin_prio(struct sk_buff *msg, const struct dpll_pin_attr
>>*attr)
>>>+{
>>>+	u32 prio;
>>>+
>>>+	if (dpll_pin_attr_prio_get(attr, &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_pin_attr *attr)
>>>+{
>>>+	unsigned int netifindex; // TODO: Should be u32?
>>>+
>>>+	if (dpll_pin_attr_netifindex_get(attr, &netifindex))
>>>+		return 0;
>>>+	if (nla_put_u32(msg, DPLLA_PIN_NETIFINDEX, netifindex))
>>>+		return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+static int
>>>+dpll_msg_add_event_change_type(struct sk_buff *msg,
>>>+			       enum dpll_event_change event)
>>>+{
>>>+	if (nla_put_s32(msg, DPLLA_CHANGE_TYPE, event))
>>>+		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_attr *attr = dpll_pin_attr_alloc();
>>>+	struct dpll_pin *parent = NULL;
>>>+	int ret;
>>>+
>>>+	if (!attr)
>>>+		return -ENOMEM;
>>>+	ret = dpll_msg_add_pin_idx(msg, dpll_pin_idx(dpll, pin));
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_description(msg,
>>dpll_pin_get_description(pin));
>>>+	if (ret)
>>>+		goto out;
>>>+	parent = dpll_pin_get_parent(pin);
>>>+	if (parent) {
>>>+		ret = dpll_msg_add_pin_parent_idx(msg, dpll_pin_idx(dpll,
>>>+								    parent));
>>>+		if (ret)
>>>+			goto out;
>>>+	}
>>>+	ret = dpll_pin_get_attr(dpll, pin, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_type(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_types_supported(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_signal_type(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_signal_types_supported(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_custom_freq(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_states(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_states_supported(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_prio(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+	ret = dpll_msg_add_pin_netifindex(msg, attr);
>>>+	if (ret)
>>>+		goto out;
>>>+out:
>>>+	dpll_pin_attr_free(attr);
>>>+
>>>+	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)
>>>+{
>>>+	struct dpll_attr *attr = dpll_attr_alloc();
>>>+	int ret = dpll_get_attr(dpll, attr);
>>>+
>>>+	if (ret)
>>>+		return -EAGAIN;
>>>+	if (dpll_msg_add_source_pin(msg, attr))
>>>+		return -EMSGSIZE;
>>>+	if (dpll_msg_add_temp(msg, attr))
>>>+		return -EMSGSIZE;
>>>+	if (dpll_msg_add_lock_status(msg, attr))
>>>+		return -EMSGSIZE;
>>>+	if (dpll_msg_add_mode(msg, attr))
>>>+		return -EMSGSIZE;
>>>+	if (dpll_msg_add_modes_supported(msg, attr))
>>>+		return -EMSGSIZE;
>>>+	if (dpll_msg_add_netifindex(msg, attr))
>>>+		return -EMSGSIZE;
>>>+
>>>+	return 0;
>>>+}
>>>+
>>>+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_DUMP_FILTER_STATUS) {
>>>+		ret = __dpll_cmd_dump_status(msg, dpll);
>>>+		if (ret)
>>>+			goto out_unlock;
>>>+	}
>>>+	if (dump_filter & DPLL_DUMP_FILTER_PINS)
>>>+		ret = __dpll_cmd_dump_pins(msg, dpll);
>>>+	dpll_unlock(dpll);
>>>+
>>>+	return ret;
>>>+out_unlock:
>>>+	dpll_unlock(dpll);
>>>+	return ret;
>>>+}
>>>+
>>>+static enum dpll_pin_type dpll_msg_read_pin_type(struct nlattr *a)
>>>+{
>>>+	return nla_get_s32(a);
>>
>>
>>No need to have this pointless boilerplate helpers, just call nla_get_x()
>>directly.
>>
>They read correct type from nlattr and return right type for each attribute,
>thus all are not pointless.
>But I will remove them as they are not reused anywhere.
>
>>
>>>+}
>>>+
>>>+static enum dpll_pin_signal_type dpll_msg_read_pin_sig_type(struct nlattr
>>*a)
>>>+{
>>>+	return nla_get_s32(a);
>>>+}
>>>+
>>>+static u32 dpll_msg_read_pin_custom_freq(struct nlattr *a)
>>>+{
>>>+	return nla_get_u32(a);
>>>+}
>>>+
>>>+static enum dpll_pin_state dpll_msg_read_pin_state(struct nlattr *a)
>>>+{
>>>+	return nla_get_s32(a);
>>>+}
>>>+
>>>+static u32 dpll_msg_read_pin_prio(struct nlattr *a)
>>>+{
>>>+	return nla_get_u32(a);
>>>+}
>>>+
>>>+static u32 dpll_msg_read_dump_filter(struct nlattr *a)
>>>+{
>>>+	return nla_get_u32(a);
>>>+}
>>>+
>>>+static int
>>>+dpll_pin_attr_from_nlattr(struct dpll_pin_attr *pa, struct genl_info
>>*info)
>>>+{
>>>+	enum dpll_pin_signal_type st;
>>>+	enum dpll_pin_state state;
>>>+	enum dpll_pin_type t;
>>>+	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_TYPE:
>>
>>Does not make sense to me. Why do you allow to set the pin type? That
>>should be something constant, according to the hardware architecture.
>>
>
>Changed as suggested, if multiple pin types are possible on the hardware, they
>shall be handled with MUX-type pin and multiple pins connected to it.
>
>>
>>>+			t = dpll_msg_read_pin_type(a);
>>>+			ret = dpll_pin_attr_type_set(pa, t);
>>>+			if (ret)
>>>+				return ret;
>>>+			break;
>>>+		case DPLLA_PIN_SIGNAL_TYPE:
>>>+			st = dpll_msg_read_pin_sig_type(a);
>>>+			ret = dpll_pin_attr_signal_type_set(pa, st);
>>>+			if (ret)
>>>+				return ret;
>>>+			break;
>>>+		case DPLLA_PIN_CUSTOM_FREQ:
>>>+			freq = dpll_msg_read_pin_custom_freq(a);
>>>+			ret = dpll_pin_attr_custom_freq_set(pa, freq);
>>>+			if (ret)
>>>+				return ret;
>>>+			break;
>>>+		case DPLLA_PIN_STATE:
>>>+			state = dpll_msg_read_pin_state(a);
>>>+			ret = dpll_pin_attr_state_set(pa, state);
>>>+			if (ret)
>>>+				return ret;
>>>+			break;
>>>+		case DPLLA_PIN_PRIO:
>>>+			prio = dpll_msg_read_pin_prio(a);
>>>+			ret = dpll_pin_attr_prio_set(pa, 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_pin_attr *old = NULL, *new = NULL, *delta = NULL;
>>>+	struct dpll_device *dpll = info->user_ptr[0];
>>>+	struct nlattr **attrs = info->attrs;
>>>+	struct dpll_pin *pin;
>>>+	int ret, pin_id;
>>>+
>>>+	if (!attrs[DPLLA_PIN_IDX])
>>>+		return -EINVAL;
>>>+	pin_id = nla_get_u32(attrs[DPLLA_PIN_IDX]);
>>>+	old = dpll_pin_attr_alloc();
>>>+	new = dpll_pin_attr_alloc();
>>>+	delta = dpll_pin_attr_alloc();
>>>+	if (!old || !new || !delta) {
>>>+		ret = -ENOMEM;
>>>+		goto mem_free;
>>>+	}
>>>+	dpll_lock(dpll);
>>>+	pin = dpll_pin_get_by_idx(dpll, pin_id);
>>>+	if (!pin) {
>>>+		ret = -ENODEV;
>>>+		goto mem_free_unlock;
>>>+	}
>>>+	ret = dpll_pin_get_attr(dpll, pin, old);
>>>+	if (ret)
>>>+		goto mem_free_unlock;
>>>+	ret = dpll_pin_attr_from_nlattr(new, info);
>>>+	if (ret)
>>>+		goto mem_free_unlock;
>>>+	ret = dpll_pin_attr_delta(delta, new, old);
>>>+	dpll_unlock(dpll);
>>>+	if (!ret)
>>>+		ret = dpll_pin_set_attr(dpll, pin, delta);
>>>+	else
>>>+		ret = -EINVAL;
>>>+
>>>+	dpll_pin_attr_free(delta);
>>>+	dpll_pin_attr_free(new);
>>>+	dpll_pin_attr_free(old);
>>>+
>>>+	return ret;
>>>+
>>>+mem_free_unlock:
>>>+	dpll_unlock(dpll);
>>>+mem_free:
>>>+	dpll_pin_attr_free(delta);
>>>+	dpll_pin_attr_free(new);
>>>+	dpll_pin_attr_free(old);
>>>+	return ret;
>>>+}
>>>+
>>>+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_attr_from_nlattr(struct dpll_attr *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_attr_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_attr_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_attr *old = NULL, *new = NULL, *delta = NULL;
>>>+	struct dpll_device *dpll = info->user_ptr[0];
>>>+	int ret;
>>>+
>>>+	old = dpll_attr_alloc();
>>>+	new = dpll_attr_alloc();
>>>+	delta = dpll_attr_alloc();
>>>+	if (!old || !new || !delta) {
>>>+		ret = -ENOMEM;
>>>+		goto mem_free;
>>>+	}
>>>+	dpll_lock(dpll);
>>>+	ret = dpll_get_attr(dpll, old);
>>>+	dpll_unlock(dpll);
>>>+	if (!ret) {
>>>+		dpll_attr_from_nlattr(new, info);
>>>+		ret = dpll_attr_delta(delta, new, old);
>>>+		if (!ret)
>>>+			ret = dpll_set_attr(dpll, delta);
>>>+	}
>>>+
>>>+mem_free:
>>>+	dpll_attr_free(old);
>>>+	dpll_attr_free(new);
>>>+	dpll_attr_free(delta);
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+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_DUMP_FILTER])
>>
>>It's not a "dump" filter for "get" callback. Btw, why do you need this
>>filtering at all? Do you expect some significant amount of pins assigned
>>to dpll so you need to filter them out? If not, leave the filtering
>>mechanism out for now to make things easier.
>>
>
>It works for both, will change name to DPLLA_FILTER for less confusion.
>
>IMHO most important is to be able to separate DPLL status from the pins,
>dumping the pins can be intensive, but once everything is configured user
>would most likely just need dpll status.

That sounds like you need 2 cmds, one to get/dump devices, the other one
to get/dump pins. Then you don't need this odd filtering.


>
>>
>>
>>>+		dump_filter =
>>>+			dpll_msg_read_dump_filter(attrs[DPLLA_DUMP_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_DUMP_FILTER];
>>>+
>>>+	if (attr)
>>>+		ctx->dump_filter = dpll_msg_read_dump_filter(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);
>>
>>You are missing some locking here. What is protecting the driver from
>>unregistering the instance right at the time you have this
>>pointer returned?
>>
>>You need some reference counting and locking scheme to be defined and
>>used.
>
>True, we need a fix here.
>
>>
>>
>>>+		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]);
>>
>>I still think that we should have 1 handle for dpll.
>>We don't need DPLLA_ID. It is not stable, not sure anyone will use it.
>>Why don't you have bus/name handle tuple similar to how we do it in
>>devlink? It proved to be working good over the years. Check out:
>>DEVLINK_ATTR_BUS_NAME
>>DEVLINK_ATTR_DEV_NAME
>>
>
>Well, don't have strong opinion here.

I do.


>
>>
>>>+
>>>+		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 dpll_param *p)
>>>+{
>>>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>>>+
>>>+	if (ret)
>>>+		return ret;
>>>+	ret = dpll_msg_add_name(p->msg, dpll_dev_name(p->dpll));
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+static int dpll_event_device_change(struct dpll_param *p)
>>>+{
>>>+	int ret = dpll_msg_add_id(p->msg, dpll_id(p->dpll));
>>>+
>>>+	if (ret)
>>>+		return ret;
>>>+	ret = dpll_msg_add_event_change_type(p->msg, p->change_type);
>>>+	if (ret)
>>>+		return ret;
>>>+	switch (p->change_type)	{
>>>+	case DPLL_CHANGE_PIN_ADD:
>>>+	case DPLL_CHANGE_PIN_DEL:
>>>+	case DPLL_CHANGE_PIN_TYPE:
>>>+	case DPLL_CHANGE_PIN_SIGNAL_TYPE:
>>>+	case DPLL_CHANGE_PIN_STATE:
>>>+	case DPLL_CHANGE_PIN_PRIO:
>>>+		ret = dpll_msg_add_pin_idx(p->msg, dpll_pin_idx(p->dpll, p-
>>>pin));
>>>+		break;
>>>+	default:
>>>+		break;
>>>+	}
>>>+
>>>+	return ret;
>>>+}
>>>+
>>>+static const cb_t event_cb[] = {
>>>+	[DPLL_EVENT_DEVICE_CREATE]	= dpll_event_device_id,
>>>+	[DPLL_EVENT_DEVICE_DELETE]	= dpll_event_device_id,
>>>+	[DPLL_EVENT_DEVICE_CHANGE]	= dpll_event_device_change,
>>>+};
>>>+
>>>+/*
>>>+ * Generic netlink DPLL event encoding
>>>+ */
>>>+static int dpll_send_event(enum dpll_event event, struct dpll_param *p)
>>>+{
>>>+	struct sk_buff *msg;
>>>+	int ret = -EMSGSIZE;
>>>+	void *hdr;
>>>+
>>>+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>>+	if (!msg)
>>>+		return -ENOMEM;
>>>+	p->msg = msg;
>>>+
>>>+	hdr = genlmsg_put(msg, 0, 0, &dpll_family, 0, event);
>>>+	if (!hdr)
>>>+		goto out_free_msg;
>>>+
>>>+	ret = event_cb[event](p);
>>>+	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)
>>>+{
>>>+	struct dpll_param p = { .dpll = dpll };
>>>+
>>>+	return dpll_send_event(DPLL_EVENT_DEVICE_CREATE, &p);
>>>+}
>>>+
>>>+int dpll_notify_device_delete(struct dpll_device *dpll)
>>>+{
>>>+	struct dpll_param p = { .dpll = dpll };
>>>+
>>>+	return dpll_send_event(DPLL_EVENT_DEVICE_DELETE, &p);
>>>+}
>>>+
>>>+int dpll_notify_device_change(struct dpll_device *dpll,
>>>+			      enum dpll_event_change event,
>>>+			      struct dpll_pin *pin)
>>>+{
>>>+	struct dpll_param p = { .dpll = dpll,
>>>+				.change_type = event,
>>>+				.pin = pin };
>>>+
>>>+	return dpll_send_event(DPLL_EVENT_DEVICE_CHANGE, &p);
>>>+}
>>>+EXPORT_SYMBOL_GPL(dpll_notify_device_change);
>>>+
>>>+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..a9cbf260624d
>>>--- /dev/null
>>>+++ b/include/linux/dpll.h
>>>@@ -0,0 +1,261 @@
>>>+/* 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/dpll_attr.h>
>>>+#include <linux/device.h>
>>>+
>>>+struct dpll_device;
>>>+struct dpll_pin;
>>>+
>>>+#define DPLL_COOKIE_LEN		10
>>>+#define PIN_IDX_INVALID		((u32)ULONG_MAX)
>>
>>Plese maintain the namespace prefix.
>>
>
>Sure.
>
>>
>>>+struct dpll_device_ops {
>>>+	int (*get)(struct dpll_device *dpll, struct dpll_attr *attr);
>>>+	int (*set)(struct dpll_device *dpll, const struct dpll_attr *attr);
>>>+};
>>>+
>>>+struct dpll_pin_ops {
>>>+	int (*get)(struct dpll_device *dpll, struct dpll_pin *pin,
>>>+		   struct dpll_pin_attr *attr);
>>>+	int (*set)(struct dpll_device *dpll, struct dpll_pin *pin,
>>>+		   const struct dpll_pin_attr *attr);
>>>+	int (*select)(struct dpll_device *dpll, struct dpll_pin *pin);
>>>+};
>>>+
>>>+enum dpll_type {
>>>+	DPLL_TYPE_UNSPEC,
>>
>>You are not in UAPI, no need of unspec here.
>>
>
>Will move into uapi as this value would be part of generated dpll
>device name, thus seems useful for userspace.

I don't follow. Could you please elaborate a bit more?


>
>>
>>>+	DPLL_TYPE_PPS,
>>>+	DPLL_TYPE_EEC,
>>
>>What exactly are these? Some comment would be really good here.
>>
>
>Sure.
>
>>
>>
>>>+
>>>+	__DPLL_TYPE_MAX
>>>+};
>>>+#define DPLL_TYPE_MAX	(__DPLL_TYPE_MAX - 1)
>>>+
>>>+/**
>>>+ * dpll_device_alloc - allocate memory for a new dpll_device object
>>>+ * @ops: pointer to dpll operations structure
>>>+ * @type: type of a dpll being allocated
>>>+ * @cookie: a system unique number for a device
>>>+ * @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: cookie, type and dev_driver_idx.
>>>+ * Finding allocated and registered dpll device is also possible with
>>>+ * the: cookie, 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 u8 cookie[DPLL_COOKIE_LEN], u8 dev_driver_idx,
>>>+		   void *priv, struct device *parent);
>>>+
>>>+/**
>>>+ * dpll_device_register - registers allocated dpll
>>>+ * @dpll: pointer to dpll
>>>+ *
>>>+ * Register the dpll on the dpll subsystem, make it available for netlink
>>>+ * API users.
>>>+ */
>>>+void dpll_device_register(struct dpll_device *dpll);
>>>+
>>>+/**
>>>+ * 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(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(struct dpll_device *dpll, 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
>>>+ * @pin_idx: index of a pin on dpll device (@dpll_pin_owner)
>>>+ *	     that is being registered on 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, u32 pin_idx,
>>>+			 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
>>>+ * @desc_len: number of chars in description
>>>+ *
>>>+ * 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, size_t
>>desc_len);
>>>+
>>>+
>>>+/**
>>>+ * 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: pointer to object to register pin with
>>>+ * @pin: pointer to allocated pin object being deregistered from dpll
>>>+ * @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,
>>>+			    struct dpll_pin *parent_pin, struct dpll_pin *pin,
>>>+			    struct dpll_pin_ops *ops, void *priv);
>>>+/**
>>>+ * dpll_device_get_by_cookie - find a dpll by its cookie
>>>+ * @cookie: cookie of dpll to search for, 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_cookie(u8 cookie[DPLL_COOKIE_LEN],
>>>+					      enum dpll_type type, u8 idx);
>>>+
>>>+/**
>>>+ * dpll_pin_get_by_description - find a pin by its description
>>>+ * @dpll: dpll device pointer
>>>+ * @description: string description 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_description(struct dpll_device *dpll,
>>>+					     const char *description);
>>>+
>>>+/**
>>>+ * 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);
>>>+
>>>+int dpll_notify_device_change(struct dpll_device *dpll,
>>>+			      enum dpll_event_change event,
>>>+			      struct dpll_pin *pin);
>>>+#endif
>>>diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
>>>new file mode 100644
>>>index 000000000000..16278618d59d
>>>--- /dev/null
>>>+++ b/include/uapi/linux/dpll.h
>>>@@ -0,0 +1,263 @@
>>>+/* 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 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_DUMP_FILTER_PINS	1
>>>+#define DPLL_DUMP_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_DUMP_FILTER - filter bitmask (int, sum of DPLL_DUMP_FILTER_*
>>defines)
>>>+ * @DPLLA_NETIFINDEX - related network interface index
>>>+ * @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_TYPE_SUPPORTED - pin types supported (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_STATE - state of pin's capabilities (enum dpll_pin_state)
>>>+ * @DPLLA_PIN_STATE_SUPPORTED - available pin's capabilities
>>>+ *	(enum dpll_pin_state)
>>>+ * @DPLLA_PIN_PRIO - priority of a pin on dpll (unsigned int)
>>>+ * @DPLLA_PIN_PARENT_IDX - if of a parent pin (unsigned int)
>>>+ * @DPLLA_CHANGE_TYPE - type of device change event
>>>+ *	(enum dpll_change_type)
>>>+ * @DPLLA_PIN_NETIFINDEX - related network interface index for the pin
>>>+ **/
>>>+enum dplla {
>>>+	DPLLA_UNSPEC,
>>>+	DPLLA_ID,
>>>+	DPLLA_NAME,
>>>+	DPLLA_MODE,
>>>+	DPLLA_MODE_SUPPORTED,
>>>+	DPLLA_SOURCE_PIN_IDX,
>>>+	DPLLA_LOCK_STATUS,
>>>+	DPLLA_TEMP,
>>>+	DPLLA_DUMP_FILTER,
>>>+	DPLLA_NETIFINDEX,
>>>+	DPLLA_PIN,
>>>+	DPLLA_PIN_IDX,
>>>+	DPLLA_PIN_DESCRIPTION,
>>>+	DPLLA_PIN_TYPE,
>>>+	DPLLA_PIN_TYPE_SUPPORTED,
>>>+	DPLLA_PIN_SIGNAL_TYPE,
>>>+	DPLLA_PIN_SIGNAL_TYPE_SUPPORTED,
>>>+	DPLLA_PIN_CUSTOM_FREQ,
>>>+	DPLLA_PIN_STATE,
>>>+	DPLLA_PIN_STATE_SUPPORTED,
>>>+	DPLLA_PIN_PRIO,
>>>+	DPLLA_PIN_PARENT_IDX,
>>>+	DPLLA_CHANGE_TYPE,
>>>+	DPLLA_PIN_NETIFINDEX,
>>>+	__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,
>>
>>Good.
>>
>>
>>>+
>>>+	__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
>>
>>As I wrote in the reply to the cover letter, I don't think we should
>>have this.
>>
>
>For now leaving.
>
>>
>>>+ * @DPLL_PIN_TYPE_EXT - external source
>>
>>Why "source" ? It could be an output too.
>>
>>
>>>+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT - ethernet port PHY's recovered clock
>>>+ * @DPLL_PIN_TYPE_INT_OSCILLATOR - device internal oscillator
>>
>>I don't follow why this is a PIN. It is internal clock of DPLL. It
>>should not be exposed as a pin.
>>
>
>May be a good point, I am leaving it but let Vadim or Jakub confirm it is
>not needed for them.
>In ice dpll doesn't have internal oscillator, as it is located externally to
>dpll. But at the same time we don't expose this pin to the user.


Why would the user case if the internal oscilator (internal to the
device) is in dpll or connected externally? It's an implementation
detail. The model does not need to handle it.

I have a feeling that you want to expose lots of hw details to the
kernel/user, that have 0 value. Don't do that.


>
>>
>>>+ * @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,
>>
>>1HZ
>>
>>>+	DPLL_PIN_SIGNAL_TYPE_10_MHZ,
>>
>>10000000Hz
>
>IMHO it is better now.
>
>>
>>>+	DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ,
>>
>>XHz
>>
>>Why don't we have this at all? There could be just u64 ATTR that carries
>>frequency in Hz.
>>
>
>May be a good point, but again let the other say the word.

What is an argument against this? It is used for SPEED in ethtool for
centuries...


>
>>
>>We are missing signal type for SYNCE_ETH_PORT pin type. Is it supposed
>>to be DPLL_PIN_SIGNAL_TYPE_UNSPEC? I think it might be if we stick with
>>having this enum.
>>
>
>I would say DPLL_PIN_SIGNAL_TYPE_CUSTOM_FREQ, or we can add something new.

It is not custom frequency. That is misleading.


>
>>
>>>+
>>>+	__DPLL_PIN_SIGNAL_TYPE_MAX,
>>>+};
>>>+
>>>+#define DPLL_PIN_SIGNAL_TYPE_MAX (__DPLL_PIN_SIGNAL_TYPE_MAX - 1)
>>>+
>>>+/* dpll_pin_state - available pin states
>>>+ *
>>>+ * @DPLL_PIN_STATE_UNSPEC - unspecified value
>>>+ * @DPLL_PIN_STATE_CONNECTED - pin connected
>>>+ * @DPLL_PIN_STATE_DISCONNECTED - pin disconnected
>>>+ * @DPLL_PIN_STATE_SOURCE - pin used as an input pin
>>>+ * @DPLL_PIN_STATE_OUTPUT - pin used as an output pin
>>>+ **/
>>>+enum dpll_pin_state {
>>>+	DPLL_PIN_STATE_UNSPEC,
>>>+	DPLL_PIN_STATE_CONNECTED,
>>>+	DPLL_PIN_STATE_DISCONNECTED,
>>>+	DPLL_PIN_STATE_SOURCE,
>>>+	DPLL_PIN_STATE_OUTPUT,
>>
>>The pin can be "connected" and "source" at the same time. How do you
>>expose that. I think we should remove "connected and just have:
>>	DPLL_PIN_STATE_DISCONNECTED,
>>	DPLL_PIN_STATE_SOURCE,
>>	DPLL_PIN_STATE_OUTPUT,
>
>Userspace will get stream of attributes with the same id:
>   DPLLA_PIN_IDX=19
>        DPLLA_PIN_DESCRIPTION=C827_0-RCLKA-3

What's this cryptic string?


>        DPLLA_PIN_TYPE=3
>        DPLLA_PIN_PARENT_IDX=2
>        DPLLA_PIN_SIGNAL_TYPE=3
>        DPLLA_PIN_SIGNAL_TYPE_SUPPORTED=3
>        DPLLA_PIN_STATE=1
>        DPLLA_PIN_STATE=3

Does not make any sense what so ever. What is should be only one. Don't
schrodinger this.


>        DPLLA_PIN_STATE_SUPPORTED=1
>        DPLLA_PIN_STATE_SUPPORTED=2
>        DPLLA_PIN_STATE_SUPPORTED=3
>
>>
>>"state" also sound odd here, as it is not really a state. It is a "mode"
>>of a pin which is controlled by the user. Could you call it "MODE"?
>>
>
>Sure, seems legit. will try.
>
>>
>>
>>>+
>>>+	__DPLL_PIN_STATE_MAX,
>>>+};
>>>+
>>>+#define DPLL_PIN_STATE_MAX (__DPLL_PIN_STATE_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_TYPE - pin type cahnged,
>>>+ * @DPLL_CHANGE_PIN_SIGNAL_TYPE pin signal type changed
>>>+ * @DPLL_CHANGE_PIN_CUSTOM_FREQ custom frequency changed
>>>+ * @DPLL_CHANGE_PIN_STATE - 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_TYPE,
>>>+	DPLL_CHANGE_PIN_SIGNAL_TYPE,
>>>+	DPLL_CHANGE_PIN_CUSTOM_FREQ,
>>>+	DPLL_CHANGE_PIN_STATE,
>>>+	DPLL_CHANGE_PIN_PRIO,
>>
>>I think it is odd to have this. With every future added attribute, you
>>are going to add a value here as well. Basically you have 1:1
>>relationship.
>>
>>I have to say I didn't see this concept in any other netlink usecase.
>>Did you?
>>
>>Why exactly do you need it? Userspace usually maintains the internal
>>object instance anyway, it can compare incoming message with that.
>>
>
>Heven't looked for it.
>If there is no other opinion we can remove most of them.
>
>>
>>
>>>+
>>>+	__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_FORCED - source can be only selected by sending a request
>>to dpll
>>
>>I would probably go rather for "MODE_MANUAL". "forced" does not sound
>>correct there.
>>
>
>Sure.
>
>>
>>>+ * @DPLL_MODE_AUTOMATIC - highest prio, valid source, auto selected by
>>dpll
>>>+ * @DPLL_MODE_HOLDOVER - dpll forced into holdover mode
>>
>>Well, when user sets this, he basically tells the device to freerun.
>>What would be different between setting this and DPLL_MODE_FREERUN?
>>
>
>We took it from ZL80032 specification:
>In freerun the dpll is not doing any synchronization with any reference inputs,
>output signal is based on the device's system clock frequency offset only.

What's "system clock" in this context?


>The freerun accuracy of the output clock is equal to the accuracy of the system
>clock.
>
>HOLDOVER (actually FORCED HOLDOVER) is possible when there was a valid signal
>and after requesting this mode: references are ignored and the DPLL must go to
>holdover (at the frequency offset of the most recent selected reference)

I assuse use would be able to select this "forced holdover" only when
the souce is locked at the time of selection. Correct?
How the behaviour actually differs betwee selecting "forced holdover"
and "freerun"?



>
>>
>>>+ * @DPLL_MODE_FREERUN - dpll driven on system clk, no holdover available
>>>+ * @DPLL_MODE_NCO - dpll driven by Numerically Controlled Oscillator
>>
>>I'm clueless what this is. Isn't this Freerun too?
>
>NCO is a freerun but it possible to control frequency, user can request
>different frequency than the one of the system clock.

How?


>
>
>Thanks,
>Arkadiusz
>>
>>
>>>+ **/
>>>+enum dpll_mode {
>>>+	DPLL_MODE_UNSPEC,
>>>+	DPLL_MODE_FORCED,
>>>+	DPLL_MODE_AUTOMATIC,
>>>+	DPLL_MODE_HOLDOVER,
>>>+	DPLL_MODE_FREERUN,
>>>+	DPLL_MODE_NCO,
>>>+
>>>+	__DPLL_MODE_MAX,
>>>+};
>>>+
>>>+#define DPLL_MODE_MAX (__DPLL_MODE_MAX - 1)
>>>+
>>>+#endif /* _UAPI_LINUX_DPLL_H */
>>>--
>>>2.27.0
>>>

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-12 13:58                     ` Jiri Pirko
@ 2023-01-09 14:43                       ` Kubalewski, Arkadiusz
  2023-01-09 16:30                         ` Jiri Pirko
  2023-01-10 20:05                         ` Jakub Kicinski
  0 siblings, 2 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-09 14:43 UTC (permalink / raw)
  To: Jiri Pirko, Jakub Kicinski
  Cc: Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, December 12, 2022 2:59 PM
>
>Fri, Dec 09, 2022 at 05:31:04PM CET, kuba@kernel.org wrote:
>>On Fri, 9 Dec 2022 15:09:08 +0100 Maciek Machnikowski wrote:
>>> On 12/9/2022 12:07 PM, Jiri Pirko wrote:
>>> > Looking at the documentation of the chips, they all have mupltiple
>DPLLs
>>> > on a die. Arkadiusz, in your proposed implementation, do you model
>each
>>> > DPLL separatelly? If yes, then I understand the urgency of need of a
>>> > shared pin. So all DPLLs sharing the pin are part of the same chip?
>>> >
>>> > Question: can we have an entity, that would be 1:1 mapped to the
>actual
>>> > device/chip here? Let's call is "a synchronizer". It would contain
>>> > multiple DPLLs, user-facing-sources(input_connector),
>>> > user-facing-outputs(output_connector), i/o pins.
>>> >
>>> > An example:
>>> >                                SYNCHRONIZER
>>> >
>>> >
>┌───────────────────────────────────────┐
>>> >                               │
>│
>>> >                               │
>│
>>> >   SyncE in connector          │              ┌─────────┐
>│     SyncE out connector
>>> >                 ┌───┐         │in pin 1      │DPLL_1   │     out pin
>1│    ┌───┐
>>> >                 │   ├─────────┼──────────────┤
>├──────────────┼────┤   │
>>> >                 │   │         │              │         │
>│    │   │
>>> >                 └───┘         │              │         │
>│    └───┘
>>> >                               │              │         │
>│
>>> >                               │           ┌──┤         │
>│
>>> >    GNSS in connector          │           │  └─────────┘
>│
>>> >                 ┌───┐         │in pin 2   │                  out pin
>2│     EXT SMA connector
>>> >                 │   ├─────────┼───────────┘
>│    ┌───┐
>>> >                 │   │         │
>┌───────────┼────┤   │
>>> >                 └───┘         │                           │
>│    │   │
>>> >                               │                           │
>│    └───┘
>>> >                               │                           │
>│
>>> >    EXT SMA connector          │                           │
>│
>>> >                 ┌───┐   mux   │in pin 3      ┌─────────┐  │
>│
>>> >                 │   ├────┬────┼───────────┐  │         │  │
>│
>>> >                 │   │    │    │           │  │DPLL_2   │  │
>│
>>> >                 └───┘    │    │           │  │         │  │
>│
>>> >                          │    │           └──┤         ├──┘
>│
>>> >                          │    │              │         │
>│
>>> >    EXT SMA connector     │    │              │         │
>│
>>> >                 ┌───┐    │    │              │         │
>│
>>> >                 │   ├────┘    │              └─────────┘
>│
>>> >                 │   │         │
>│
>>> >                 └───┘
>└───────────────────────────────────────┘
>>> >
>>> > Do I get that remotelly correct?
>>>
>>> It looks goot, hence two corrections are needed:
>>> - all inputs can go to all DPLLs, and a single source can drive more
>>>   than one DPLL
>>> - The external mux for SMA connector should not be a part of the
>>>   Synchronizer subsystem - I believe there's already a separate MUX
>>>   subsystem in the kernel and all external connections should be handled
>>>   by a devtree or a similar concept.
>>>
>>> The only "muxing" thing that could potentially be modeled is a
>>> synchronizer output to synchronizer input relation. Some synchronizers
>>> does that internally and can use the output of one DPLL as a source for
>>> another.
>>
>>My experience with DT and muxes is rapidly aging, have you worked with
>>those recently? From what I remember the muxes were really.. "embedded"
>>and static compared to what we want here.
>
>Why do you think we need something "non-static"? The mux is part of the
>board, isn't it? That sounds quite static to me.
>
>
>>
>>Using DT may work nicely for defining the topology, but for config we
>>still need a different mechanism.
>
>"config" of what? Each item in topology would be configure according to
>the item type, won't it?
>
>[...]


Hi guys,

We have been trying to figure out feasibility of new approach proposed on our
latest meeting - to have a single object which encapsulates multiple DPLLs.

Please consider following example:

Shared common inputs:                                      
i0 - GPS  / external                                       
i1 - SMA1 / external                                       
i2 - SMA2 / external                                       
i3 - MUX0 / clk recovered from PHY0.X driven by MAC0       
i4 - MUX1 / clk recovered from PHY1.X driven by MAC1       

+---------------------------------------------------------+
| Channel A / FW0             +---+                       |
|                         i0--|   |                       |
|         +---+               |   |                       |
| PHY0.0--|   |           i1--| D |                       |
|         |   |               | P |                       |
| PHY0.1--| M |           i2--| L |   +---+   +--------+  |
|         | U |               | L |---|   |---| PHY0.0 |--|
| PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
|         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
| ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
|         |   | |+------+     +---+   | C |---| PHY0.2 |--|
| PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
|         +---+ |             |   |---|   |---| ...    |--|
|               |         i1--| D |   |   |   +--------+  |
|               |             | P |---|   |---| PHY0.7 |--|
|               |         i2--| L |   +---+   +--------+  |
|               |             | L |                       |
|               \---------i3--| 1 |                       |
|                +------+     |   |                       |
|                | MUX1 |-i4--|   |                       |
|                +------+     +---+                       |
+---------------------------------------------------------+
| Channel B / FW1             +---+                       |
|                         i0--|   |                       |
|                             |   |                       |
|                         i1--| D |                       |
|         +---+               | P |                       |
| PHY1.0--|   |           i2--| L |   +---+   +--------+  |
|         |   |  +------+     | L |---|   |---| PHY1.0 |--|
| PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
|         | U |  +------+     |   |---| M |---| PHY1.1 |--|
| PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
|         | 1 | |             +---+   | C |---| PHY1.2 |--|
| ...   --|   | |         i0--|   |   | 1 |   +--------+  |
|         |   | |             |   |---|   |---| ...    |--|
| PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
|         +---+ |             | P |---|   |---| PHY1.7 |--|
|               |         i2--| L |   +---+   +--------+  |
|               |+------+     | L |                       |
|               || MUX0 |-i3--| 1 |                       |
|               |+------+     |   |                       |
|               \---------i4--|   |                       |
|                             +---+                       |
+---------------------------------------------------------+

This is a simplified network switch board example.
It has 2 synchronization channels, where each channel:
- provides clk to 8 PHYs driven by separated MAC chips,
- controls 2 DPLLs.

Basically only given FW has control over its PHYs, so also a control over it's
MUX inputs.
All external sources are shared between the channels.

This is why we believe it is not best idea to enclose multiple DPLLs with one
object:
- sources are shared even if DPLLs are not a single synchronizer chip,
- control over specific MUX type input shall be controllable from different
driver/firmware instances.

As we know the proposal of having multiple DPLLs in one object was a try to
simplify currently implemented shared pins. We fully support idea of having
interfaces as simple as possible, but at the same time they shall be flexible
enough to serve many use cases.

Right now the use case of single "synchronizer chip" is possible (2 DPLLs with
shared inputs), as well as multiple synchronizer chips with shared inputs.

If we would entirely get rid of sharing pins idea and instead allowed only to
have multiple DPLLs in one object, we would fall back to the problem where
change on one input is braking another "synchronizer chip" input.
I.e. considering above scheme, user configured both channels to use SMA1 1MHz.
If SMA1 input is changed to 10MHz, all DPLLs are affected, thus all using that
input shall be notified, as long as that input is shared.
For the drivers that have single point of control over dpll, they might just
skip those requests. But if there are multiple firmware instances controlling
multiple DPLLs, they would process it independently.

Current implementation is the most flexible and least complex for the level of
flexibility it provides.

BR, Happy new year!
Arkadiusz

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-09 14:43                       ` Kubalewski, Arkadiusz
@ 2023-01-09 16:30                         ` Jiri Pirko
  2023-01-10 10:54                           ` Kubalewski, Arkadiusz
  2023-01-10 20:05                         ` Jakub Kicinski
  1 sibling, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2023-01-09 16:30 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

Mon, Jan 09, 2023 at 03:43:01PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, December 12, 2022 2:59 PM
>>
>>Fri, Dec 09, 2022 at 05:31:04PM CET, kuba@kernel.org wrote:
>>>On Fri, 9 Dec 2022 15:09:08 +0100 Maciek Machnikowski wrote:
>>>> On 12/9/2022 12:07 PM, Jiri Pirko wrote:
>>>> > Looking at the documentation of the chips, they all have mupltiple
>>DPLLs
>>>> > on a die. Arkadiusz, in your proposed implementation, do you model
>>each
>>>> > DPLL separatelly? If yes, then I understand the urgency of need of a
>>>> > shared pin. So all DPLLs sharing the pin are part of the same chip?
>>>> >
>>>> > Question: can we have an entity, that would be 1:1 mapped to the
>>actual
>>>> > device/chip here? Let's call is "a synchronizer". It would contain
>>>> > multiple DPLLs, user-facing-sources(input_connector),
>>>> > user-facing-outputs(output_connector), i/o pins.
>>>> >
>>>> > An example:
>>>> >                                SYNCHRONIZER
>>>> >
>>>> >
>>┌───────────────────────────────────────┐
>>>> >                               │
>>│
>>>> >                               │
>>│
>>>> >   SyncE in connector          │              ┌─────────┐
>>│     SyncE out connector
>>>> >                 ┌───┐         │in pin 1      │DPLL_1   │     out pin
>>1│    ┌───┐
>>>> >                 │   ├─────────┼──────────────┤
>>├──────────────┼────┤   │
>>>> >                 │   │         │              │         │
>>│    │   │
>>>> >                 └───┘         │              │         │
>>│    └───┘
>>>> >                               │              │         │
>>│
>>>> >                               │           ┌──┤         │
>>│
>>>> >    GNSS in connector          │           │  └─────────┘
>>│
>>>> >                 ┌───┐         │in pin 2   │                  out pin
>>2│     EXT SMA connector
>>>> >                 │   ├─────────┼───────────┘
>>│    ┌───┐
>>>> >                 │   │         │
>>┌───────────┼────┤   │
>>>> >                 └───┘         │                           │
>>│    │   │
>>>> >                               │                           │
>>│    └───┘
>>>> >                               │                           │
>>│
>>>> >    EXT SMA connector          │                           │
>>│
>>>> >                 ┌───┐   mux   │in pin 3      ┌─────────┐  │
>>│
>>>> >                 │   ├────┬────┼───────────┐  │         │  │
>>│
>>>> >                 │   │    │    │           │  │DPLL_2   │  │
>>│
>>>> >                 └───┘    │    │           │  │         │  │
>>│
>>>> >                          │    │           └──┤         ├──┘
>>│
>>>> >                          │    │              │         │
>>│
>>>> >    EXT SMA connector     │    │              │         │
>>│
>>>> >                 ┌───┐    │    │              │         │
>>│
>>>> >                 │   ├────┘    │              └─────────┘
>>│
>>>> >                 │   │         │
>>│
>>>> >                 └───┘
>>└───────────────────────────────────────┘
>>>> >
>>>> > Do I get that remotelly correct?
>>>>
>>>> It looks goot, hence two corrections are needed:
>>>> - all inputs can go to all DPLLs, and a single source can drive more
>>>>   than one DPLL
>>>> - The external mux for SMA connector should not be a part of the
>>>>   Synchronizer subsystem - I believe there's already a separate MUX
>>>>   subsystem in the kernel and all external connections should be handled
>>>>   by a devtree or a similar concept.
>>>>
>>>> The only "muxing" thing that could potentially be modeled is a
>>>> synchronizer output to synchronizer input relation. Some synchronizers
>>>> does that internally and can use the output of one DPLL as a source for
>>>> another.
>>>
>>>My experience with DT and muxes is rapidly aging, have you worked with
>>>those recently? From what I remember the muxes were really.. "embedded"
>>>and static compared to what we want here.
>>
>>Why do you think we need something "non-static"? The mux is part of the
>>board, isn't it? That sounds quite static to me.
>>
>>
>>>
>>>Using DT may work nicely for defining the topology, but for config we
>>>still need a different mechanism.
>>
>>"config" of what? Each item in topology would be configure according to
>>the item type, won't it?
>>
>>[...]
>
>
>Hi guys,
>
>We have been trying to figure out feasibility of new approach proposed on our
>latest meeting - to have a single object which encapsulates multiple DPLLs.
>
>Please consider following example:
>
>Shared common inputs:                                      
>i0 - GPS  / external                                       
>i1 - SMA1 / external                                       
>i2 - SMA2 / external                                       
>i3 - MUX0 / clk recovered from PHY0.X driven by MAC0       
>i4 - MUX1 / clk recovered from PHY1.X driven by MAC1       
>
>+---------------------------------------------------------+
>| Channel A / FW0             +---+                       |
>|                         i0--|   |                       |
>|         +---+               |   |                       |
>| PHY0.0--|   |           i1--| D |                       |
>|         |   |               | P |                       |
>| PHY0.1--| M |           i2--| L |   +---+   +--------+  |
>|         | U |               | L |---|   |---| PHY0.0 |--|
>| PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
>|         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
>| ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
>|         |   | |+------+     +---+   | C |---| PHY0.2 |--|
>| PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
>|         +---+ |             |   |---|   |---| ...    |--|
>|               |         i1--| D |   |   |   +--------+  |
>|               |             | P |---|   |---| PHY0.7 |--|
>|               |         i2--| L |   +---+   +--------+  |
>|               |             | L |                       |
>|               \---------i3--| 1 |                       |
>|                +------+     |   |                       |
>|                | MUX1 |-i4--|   |                       |
>|                +------+     +---+                       |
>+---------------------------------------------------------+
>| Channel B / FW1             +---+                       |
>|                         i0--|   |                       |
>|                             |   |                       |
>|                         i1--| D |                       |
>|         +---+               | P |                       |
>| PHY1.0--|   |           i2--| L |   +---+   +--------+  |
>|         |   |  +------+     | L |---|   |---| PHY1.0 |--|
>| PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
>|         | U |  +------+     |   |---| M |---| PHY1.1 |--|
>| PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
>|         | 1 | |             +---+   | C |---| PHY1.2 |--|
>| ...   --|   | |         i0--|   |   | 1 |   +--------+  |
>|         |   | |             |   |---|   |---| ...    |--|
>| PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
>|         +---+ |             | P |---|   |---| PHY1.7 |--|
>|               |         i2--| L |   +---+   +--------+  |
>|               |+------+     | L |                       |
>|               || MUX0 |-i3--| 1 |                       |
>|               |+------+     |   |                       |
>|               \---------i4--|   |                       |
>|                             +---+                       |
>+---------------------------------------------------------+

What is "a channel" here? Are these 2 channels part of the same physival
chip? Could you add the synchronizer chip/device entities to your drawing?


>
>This is a simplified network switch board example.
>It has 2 synchronization channels, where each channel:
>- provides clk to 8 PHYs driven by separated MAC chips,
>- controls 2 DPLLs.
>
>Basically only given FW has control over its PHYs, so also a control over it's
>MUX inputs.
>All external sources are shared between the channels.
>
>This is why we believe it is not best idea to enclose multiple DPLLs with one
>object:
>- sources are shared even if DPLLs are not a single synchronizer chip,
>- control over specific MUX type input shall be controllable from different
>driver/firmware instances.
>
>As we know the proposal of having multiple DPLLs in one object was a try to
>simplify currently implemented shared pins. We fully support idea of having
>interfaces as simple as possible, but at the same time they shall be flexible
>enough to serve many use cases.
>
>Right now the use case of single "synchronizer chip" is possible (2 DPLLs with
>shared inputs), as well as multiple synchronizer chips with shared inputs.
>
>If we would entirely get rid of sharing pins idea and instead allowed only to
>have multiple DPLLs in one object, we would fall back to the problem where
>change on one input is braking another "synchronizer chip" input.
>I.e. considering above scheme, user configured both channels to use SMA1 1MHz.
>If SMA1 input is changed to 10MHz, all DPLLs are affected, thus all using that

You say "SMA1 input *is changed*". Could you add to your drawing:
1) Who is the one triggering the change.
2) Entity that manages the SMA input and applies the configuration.


>input shall be notified, as long as that input is shared.
>For the drivers that have single point of control over dpll, they might just
>skip those requests. But if there are multiple firmware instances controlling
>multiple DPLLs, they would process it independently.
>
>Current implementation is the most flexible and least complex for the level of
>flexibility it provides.
>
>BR, Happy new year!
>Arkadiusz

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-09 16:30                         ` Jiri Pirko
@ 2023-01-10 10:54                           ` Kubalewski, Arkadiusz
  2023-01-10 14:28                             ` Jiri Pirko
       [not found]                             ` <645a5bfd-0092-2f39-0ff2-3ffb27ccf8fe@machnikowski.net>
  0 siblings, 2 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-10 10:54 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Monday, January 9, 2023 5:30 PM
>
>Mon, Jan 09, 2023 at 03:43:01PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Monday, December 12, 2022 2:59 PM
>>>
>>>Fri, Dec 09, 2022 at 05:31:04PM CET, kuba@kernel.org wrote:
>>>>On Fri, 9 Dec 2022 15:09:08 +0100 Maciek Machnikowski wrote:
>>>>> On 12/9/2022 12:07 PM, Jiri Pirko wrote:
>>>>> > Looking at the documentation of the chips, they all have mupltiple
>>>DPLLs
>>>>> > on a die. Arkadiusz, in your proposed implementation, do you model
>>>each
>>>>> > DPLL separatelly? If yes, then I understand the urgency of need of a
>>>>> > shared pin. So all DPLLs sharing the pin are part of the same chip?
>>>>> >
>>>>> > Question: can we have an entity, that would be 1:1 mapped to the
>>>actual
>>>>> > device/chip here? Let's call is "a synchronizer". It would contain
>>>>> > multiple DPLLs, user-facing-sources(input_connector),
>>>>> > user-facing-outputs(output_connector), i/o pins.
>>>>> >
>>>>> > An example:
>>>>> >                                SYNCHRONIZER
>>>>> >
>>>>> >
>>>┌───────────────────────────────────────┐
>>>>> >                               │
>>>│
>>>>> >                               │
>>>│
>>>>> >   SyncE in connector          │              ┌─────────┐
>>>│     SyncE out connector
>>>>> >                 ┌───┐         │in pin 1      │DPLL_1   │     out pin
>>>1│    ┌───┐
>>>>> >                 │   ├─────────┼──────────────┤
>>>├──────────────┼────┤   │
>>>>> >                 │   │         │              │         │
>>>│    │   │
>>>>> >                 └───┘         │              │         │
>>>│    └───┘
>>>>> >                               │              │         │
>>>│
>>>>> >                               │           ┌──┤         │
>>>│
>>>>> >    GNSS in connector          │           │  └─────────┘
>>>│
>>>>> >                 ┌───┐         │in pin 2   │                  out pin
>>>2│     EXT SMA connector
>>>>> >                 │   ├─────────┼───────────┘
>>>│    ┌───┐
>>>>> >                 │   │         │
>>>┌───────────┼────┤   │
>>>>> >                 └───┘         │                           │
>>>│    │   │
>>>>> >                               │                           │
>>>│    └───┘
>>>>> >                               │                           │
>>>│
>>>>> >    EXT SMA connector          │                           │
>>>│
>>>>> >                 ┌───┐   mux   │in pin 3      ┌─────────┐  │
>>>│
>>>>> >                 │   ├────┬────┼───────────┐  │         │  │
>>>│
>>>>> >                 │   │    │    │           │  │DPLL_2   │  │
>>>│
>>>>> >                 └───┘    │    │           │  │         │  │
>>>│
>>>>> >                          │    │           └──┤         ├──┘
>>>│
>>>>> >                          │    │              │         │
>>>│
>>>>> >    EXT SMA connector     │    │              │         │
>>>│
>>>>> >                 ┌───┐    │    │              │         │
>>>│
>>>>> >                 │   ├────┘    │              └─────────┘
>>>│
>>>>> >                 │   │         │
>>>│
>>>>> >                 └───┘
>>>└───────────────────────────────────────┘
>>>>> >
>>>>> > Do I get that remotelly correct?
>>>>>
>>>>> It looks goot, hence two corrections are needed:
>>>>> - all inputs can go to all DPLLs, and a single source can drive more
>>>>>   than one DPLL
>>>>> - The external mux for SMA connector should not be a part of the
>>>>>   Synchronizer subsystem - I believe there's already a separate MUX
>>>>>   subsystem in the kernel and all external connections should be
>handled
>>>>>   by a devtree or a similar concept.
>>>>>
>>>>> The only "muxing" thing that could potentially be modeled is a
>>>>> synchronizer output to synchronizer input relation. Some synchronizers
>>>>> does that internally and can use the output of one DPLL as a source
>for
>>>>> another.
>>>>
>>>>My experience with DT and muxes is rapidly aging, have you worked with
>>>>those recently? From what I remember the muxes were really.. "embedded"
>>>>and static compared to what we want here.
>>>
>>>Why do you think we need something "non-static"? The mux is part of the
>>>board, isn't it? That sounds quite static to me.
>>>
>>>
>>>>
>>>>Using DT may work nicely for defining the topology, but for config we
>>>>still need a different mechanism.
>>>
>>>"config" of what? Each item in topology would be configure according to
>>>the item type, won't it?
>>>
>>>[...]
>>
>>
>>Hi guys,
>>
>>We have been trying to figure out feasibility of new approach proposed on
>our
>>latest meeting - to have a single object which encapsulates multiple
>DPLLs.
>>
>>Please consider following example:
>>
>>Shared common inputs:
>>i0 - GPS  / external
>>i1 - SMA1 / external
>>i2 - SMA2 / external
>>i3 - MUX0 / clk recovered from PHY0.X driven by MAC0
>>i4 - MUX1 / clk recovered from PHY1.X driven by MAC1
>>
>>+---------------------------------------------------------+
>>| Channel A / FW0             +---+                       |
>>|                         i0--|   |                       |
>>|         +---+               |   |                       |
>>| PHY0.0--|   |           i1--| D |                       |
>>|         |   |               | P |                       |
>>| PHY0.1--| M |           i2--| L |   +---+   +--------+  |
>>|         | U |               | L |---|   |---| PHY0.0 |--|
>>| PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
>>|         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
>>| ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
>>|         |   | |+------+     +---+   | C |---| PHY0.2 |--|
>>| PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
>>|         +---+ |             |   |---|   |---| ...    |--|
>>|               |         i1--| D |   |   |   +--------+  |
>>|               |             | P |---|   |---| PHY0.7 |--|
>>|               |         i2--| L |   +---+   +--------+  |
>>|               |             | L |                       |
>>|               \---------i3--| 1 |                       |
>>|                +------+     |   |                       |
>>|                | MUX1 |-i4--|   |                       |
>>|                +------+     +---+                       |
>>+---------------------------------------------------------+
>>| Channel B / FW1             +---+                       |
>>|                         i0--|   |                       |
>>|                             |   |                       |
>>|                         i1--| D |                       |
>>|         +---+               | P |                       |
>>| PHY1.0--|   |           i2--| L |   +---+   +--------+  |
>>|         |   |  +------+     | L |---|   |---| PHY1.0 |--|
>>| PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
>>|         | U |  +------+     |   |---| M |---| PHY1.1 |--|
>>| PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
>>|         | 1 | |             +---+   | C |---| PHY1.2 |--|
>>| ...   --|   | |         i0--|   |   | 1 |   +--------+  |
>>|         |   | |             |   |---|   |---| ...    |--|
>>| PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
>>|         +---+ |             | P |---|   |---| PHY1.7 |--|
>>|               |         i2--| L |   +---+   +--------+  |
>>|               |+------+     | L |                       |
>>|               || MUX0 |-i3--| 1 |                       |
>>|               |+------+     |   |                       |
>>|               \---------i4--|   |                       |
>>|                             +---+                       |
>>+---------------------------------------------------------+
>
>What is "a channel" here? Are these 2 channels part of the same physival
>chip? Could you add the synchronizer chip/device entities to your drawing?
>

No.
A "Synchronization Channel" on a switch would allow to separate groups
of physical ports. Each channel/group has own "Synchronizer Chip", which is
used to drive PHY clocks of that group.

"Synchronizer chip" would be the 2 DPLLs on old draw, something like this:
+--------------------------------------------------------------+
| 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--|   |                                                |
|         +---+                                                |
+--------------------------------------------------------------+
Also, please keep in mind that is an example, there could be easily 4
(or more) channels wired similarly.

>
>>
>>This is a simplified network switch board example.
>>It has 2 synchronization channels, where each channel:
>>- provides clk to 8 PHYs driven by separated MAC chips,
>>- controls 2 DPLLs.
>>
>>Basically only given FW has control over its PHYs, so also a control over
>it's
>>MUX inputs.
>>All external sources are shared between the channels.
>>
>>This is why we believe it is not best idea to enclose multiple DPLLs with
>one
>>object:
>>- sources are shared even if DPLLs are not a single synchronizer chip,
>>- control over specific MUX type input shall be controllable from
>different
>>driver/firmware instances.
>>
>>As we know the proposal of having multiple DPLLs in one object was a try
>to
>>simplify currently implemented shared pins. We fully support idea of
>having
>>interfaces as simple as possible, but at the same time they shall be
>flexible
>>enough to serve many use cases.
>>
>>Right now the use case of single "synchronizer chip" is possible (2 DPLLs
>with
>>shared inputs), as well as multiple synchronizer chips with shared inputs.
>>
>>If we would entirely get rid of sharing pins idea and instead allowed only
>to
>>have multiple DPLLs in one object, we would fall back to the problem where
>>change on one input is braking another "synchronizer chip" input.
>>I.e. considering above scheme, user configured both channels to use SMA1
>1MHz.
>>If SMA1 input is changed to 10MHz, all DPLLs are affected, thus all using
>that
>
>You say "SMA1 input *is changed*". Could you add to your drawing:
>1) Who is the one triggering the change.
>2) Entity that manages the SMA input and applies the configuration.
>

A user or some tool, this change requires to switch a frequency on a signal
generator connected to that SMA1. Whatever would make the change is an external
entity here. The draw show connections on board, don't see a point on having a
external signal generator or user connected to the board :)

If something is not clear, we could prepare some different draw, please just
let me know what exactly we want to see. It sound like a sequence diagram?

Thanks!
Arkadiusz

>
>>input shall be notified, as long as that input is shared.
>>For the drivers that have single point of control over dpll, they might
>just
>>skip those requests. But if there are multiple firmware instances
>controlling
>>multiple DPLLs, they would process it independently.
>>
>>Current implementation is the most flexible and least complex for the
>level of
>>flexibility it provides.
>>
>>BR, Happy new year!
>>Arkadiusz

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-10 10:54                           ` Kubalewski, Arkadiusz
@ 2023-01-10 14:28                             ` Jiri Pirko
       [not found]                             ` <645a5bfd-0092-2f39-0ff2-3ffb27ccf8fe@machnikowski.net>
  1 sibling, 0 replies; 87+ messages in thread
From: Jiri Pirko @ 2023-01-10 14:28 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

Tue, Jan 10, 2023 at 11:54:20AM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Monday, January 9, 2023 5:30 PM
>>
>>Mon, Jan 09, 2023 at 03:43:01PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Monday, December 12, 2022 2:59 PM
>>>>
>>>>Fri, Dec 09, 2022 at 05:31:04PM CET, kuba@kernel.org wrote:
>>>>>On Fri, 9 Dec 2022 15:09:08 +0100 Maciek Machnikowski wrote:
>>>>>> On 12/9/2022 12:07 PM, Jiri Pirko wrote:
>>>>>> > Looking at the documentation of the chips, they all have mupltiple
>>>>DPLLs
>>>>>> > on a die. Arkadiusz, in your proposed implementation, do you model
>>>>each
>>>>>> > DPLL separatelly? If yes, then I understand the urgency of need of a
>>>>>> > shared pin. So all DPLLs sharing the pin are part of the same chip?
>>>>>> >
>>>>>> > Question: can we have an entity, that would be 1:1 mapped to the
>>>>actual
>>>>>> > device/chip here? Let's call is "a synchronizer". It would contain
>>>>>> > multiple DPLLs, user-facing-sources(input_connector),
>>>>>> > user-facing-outputs(output_connector), i/o pins.
>>>>>> >
>>>>>> > An example:
>>>>>> >                                SYNCHRONIZER
>>>>>> >
>>>>>> >
>>>>┌───────────────────────────────────────┐
>>>>>> >                               │
>>>>│
>>>>>> >                               │
>>>>│
>>>>>> >   SyncE in connector          │              ┌─────────┐
>>>>│     SyncE out connector
>>>>>> >                 ┌───┐         │in pin 1      │DPLL_1   │     out pin
>>>>1│    ┌───┐
>>>>>> >                 │   ├─────────┼──────────────┤
>>>>├──────────────┼────┤   │
>>>>>> >                 │   │         │              │         │
>>>>│    │   │
>>>>>> >                 └───┘         │              │         │
>>>>│    └───┘
>>>>>> >                               │              │         │
>>>>│
>>>>>> >                               │           ┌──┤         │
>>>>│
>>>>>> >    GNSS in connector          │           │  └─────────┘
>>>>│
>>>>>> >                 ┌───┐         │in pin 2   │                  out pin
>>>>2│     EXT SMA connector
>>>>>> >                 │   ├─────────┼───────────┘
>>>>│    ┌───┐
>>>>>> >                 │   │         │
>>>>┌───────────┼────┤   │
>>>>>> >                 └───┘         │                           │
>>>>│    │   │
>>>>>> >                               │                           │
>>>>│    └───┘
>>>>>> >                               │                           │
>>>>│
>>>>>> >    EXT SMA connector          │                           │
>>>>│
>>>>>> >                 ┌───┐   mux   │in pin 3      ┌─────────┐  │
>>>>│
>>>>>> >                 │   ├────┬────┼───────────┐  │         │  │
>>>>│
>>>>>> >                 │   │    │    │           │  │DPLL_2   │  │
>>>>│
>>>>>> >                 └───┘    │    │           │  │         │  │
>>>>│
>>>>>> >                          │    │           └──┤         ├──┘
>>>>│
>>>>>> >                          │    │              │         │
>>>>│
>>>>>> >    EXT SMA connector     │    │              │         │
>>>>│
>>>>>> >                 ┌───┐    │    │              │         │
>>>>│
>>>>>> >                 │   ├────┘    │              └─────────┘
>>>>│
>>>>>> >                 │   │         │
>>>>│
>>>>>> >                 └───┘
>>>>└───────────────────────────────────────┘
>>>>>> >
>>>>>> > Do I get that remotelly correct?
>>>>>>
>>>>>> It looks goot, hence two corrections are needed:
>>>>>> - all inputs can go to all DPLLs, and a single source can drive more
>>>>>>   than one DPLL
>>>>>> - The external mux for SMA connector should not be a part of the
>>>>>>   Synchronizer subsystem - I believe there's already a separate MUX
>>>>>>   subsystem in the kernel and all external connections should be
>>handled
>>>>>>   by a devtree or a similar concept.
>>>>>>
>>>>>> The only "muxing" thing that could potentially be modeled is a
>>>>>> synchronizer output to synchronizer input relation. Some synchronizers
>>>>>> does that internally and can use the output of one DPLL as a source
>>for
>>>>>> another.
>>>>>
>>>>>My experience with DT and muxes is rapidly aging, have you worked with
>>>>>those recently? From what I remember the muxes were really.. "embedded"
>>>>>and static compared to what we want here.
>>>>
>>>>Why do you think we need something "non-static"? The mux is part of the
>>>>board, isn't it? That sounds quite static to me.
>>>>
>>>>
>>>>>
>>>>>Using DT may work nicely for defining the topology, but for config we
>>>>>still need a different mechanism.
>>>>
>>>>"config" of what? Each item in topology would be configure according to
>>>>the item type, won't it?
>>>>
>>>>[...]
>>>
>>>
>>>Hi guys,
>>>
>>>We have been trying to figure out feasibility of new approach proposed on
>>our
>>>latest meeting - to have a single object which encapsulates multiple
>>DPLLs.
>>>
>>>Please consider following example:
>>>
>>>Shared common inputs:
>>>i0 - GPS  / external
>>>i1 - SMA1 / external
>>>i2 - SMA2 / external
>>>i3 - MUX0 / clk recovered from PHY0.X driven by MAC0
>>>i4 - MUX1 / clk recovered from PHY1.X driven by MAC1
>>>
>>>+---------------------------------------------------------+
>>>| Channel A / FW0             +---+                       |
>>>|                         i0--|   |                       |
>>>|         +---+               |   |                       |
>>>| PHY0.0--|   |           i1--| D |                       |
>>>|         |   |               | P |                       |
>>>| PHY0.1--| M |           i2--| L |   +---+   +--------+  |
>>>|         | U |               | L |---|   |---| PHY0.0 |--|
>>>| PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
>>>|         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
>>>| ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
>>>|         |   | |+------+     +---+   | C |---| PHY0.2 |--|
>>>| PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
>>>|         +---+ |             |   |---|   |---| ...    |--|
>>>|               |         i1--| D |   |   |   +--------+  |
>>>|               |             | P |---|   |---| PHY0.7 |--|
>>>|               |         i2--| L |   +---+   +--------+  |
>>>|               |             | L |                       |
>>>|               \---------i3--| 1 |                       |
>>>|                +------+     |   |                       |
>>>|                | MUX1 |-i4--|   |                       |
>>>|                +------+     +---+                       |
>>>+---------------------------------------------------------+
>>>| Channel B / FW1             +---+                       |
>>>|                         i0--|   |                       |
>>>|                             |   |                       |
>>>|                         i1--| D |                       |
>>>|         +---+               | P |                       |
>>>| PHY1.0--|   |           i2--| L |   +---+   +--------+  |
>>>|         |   |  +------+     | L |---|   |---| PHY1.0 |--|
>>>| PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
>>>|         | U |  +------+     |   |---| M |---| PHY1.1 |--|
>>>| PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
>>>|         | 1 | |             +---+   | C |---| PHY1.2 |--|
>>>| ...   --|   | |         i0--|   |   | 1 |   +--------+  |
>>>|         |   | |             |   |---|   |---| ...    |--|
>>>| PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
>>>|         +---+ |             | P |---|   |---| PHY1.7 |--|
>>>|               |         i2--| L |   +---+   +--------+  |
>>>|               |+------+     | L |                       |
>>>|               || MUX0 |-i3--| 1 |                       |
>>>|               |+------+     |   |                       |
>>>|               \---------i4--|   |                       |
>>>|                             +---+                       |
>>>+---------------------------------------------------------+
>>
>>What is "a channel" here? Are these 2 channels part of the same physival
>>chip? Could you add the synchronizer chip/device entities to your drawing?
>>
>
>No.
>A "Synchronization Channel" on a switch would allow to separate groups
>of physical ports. Each channel/group has own "Synchronizer Chip", which is
>used to drive PHY clocks of that group.
>
>"Synchronizer chip" would be the 2 DPLLs on old draw, something like this:
>+--------------------------------------------------------------+
>| 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--|   |                                                |
>|         +---+                                                |
>+--------------------------------------------------------------+
>Also, please keep in mind that is an example, there could be easily 4
>(or more) channels wired similarly.


Good. So there are 2 synchronizers out there, each has a list of pins
and contain multiple dplls. 1 synchronizer is 1 device. No pin sharing
between devices, just between dplls inside one device. That eliminates
this odd concept of flying pin. Good.


>
>>
>>>
>>>This is a simplified network switch board example.
>>>It has 2 synchronization channels, where each channel:
>>>- provides clk to 8 PHYs driven by separated MAC chips,
>>>- controls 2 DPLLs.
>>>
>>>Basically only given FW has control over its PHYs, so also a control over
>>it's
>>>MUX inputs.
>>>All external sources are shared between the channels.
>>>
>>>This is why we believe it is not best idea to enclose multiple DPLLs with
>>one
>>>object:
>>>- sources are shared even if DPLLs are not a single synchronizer chip,
>>>- control over specific MUX type input shall be controllable from
>>different
>>>driver/firmware instances.
>>>
>>>As we know the proposal of having multiple DPLLs in one object was a try
>>to
>>>simplify currently implemented shared pins. We fully support idea of
>>having
>>>interfaces as simple as possible, but at the same time they shall be
>>flexible
>>>enough to serve many use cases.
>>>
>>>Right now the use case of single "synchronizer chip" is possible (2 DPLLs

Btw, fix your email client not to mangle the text you reply to with line
breaks like this one.


>>with
>>>shared inputs), as well as multiple synchronizer chips with shared inputs.
>>>
>>>If we would entirely get rid of sharing pins idea and instead allowed only
>>to
>>>have multiple DPLLs in one object, we would fall back to the problem where
>>>change on one input is braking another "synchronizer chip" input.
>>>I.e. considering above scheme, user configured both channels to use SMA1
>>1MHz.
>>>If SMA1 input is changed to 10MHz, all DPLLs are affected, thus all using
>>that
>>
>>You say "SMA1 input *is changed*". Could you add to your drawing:
>>1) Who is the one triggering the change.
>>2) Entity that manages the SMA input and applies the configuration.
>>
>
>A user or some tool, this change requires to switch a frequency on a signal
>generator connected to that SMA1. Whatever would make the change is an external
>entity here. The draw show connections on board, don't see a point on having a
>external signal generator or user connected to the board :)
>
>If something is not clear, we could prepare some different draw, please just
>let me know what exactly we want to see. It sound like a sequence diagram?


I think I got it. The pins are shared between DPLLS within single
synchronizer entity. That clears up my modeling concerns. Also it makes
your code much simplier, you don't need special shared pin beast with
reference counting etc. You just have a pin with synchronizer entity as
owner and expose the linkage inside this synchronizer entity between
individual DPLLs and pins.


>
>Thanks!
>Arkadiusz
>
>>
>>>input shall be notified, as long as that input is shared.
>>>For the drivers that have single point of control over dpll, they might
>>just
>>>skip those requests. But if there are multiple firmware instances
>>controlling
>>>multiple DPLLs, they would process it independently.
>>>
>>>Current implementation is the most flexible and least complex for the
>>level of
>>>flexibility it provides.
>>>
>>>BR, Happy new year!
>>>Arkadiusz

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-09 14:43                       ` Kubalewski, Arkadiusz
  2023-01-09 16:30                         ` Jiri Pirko
@ 2023-01-10 20:05                         ` Jakub Kicinski
  2023-01-11  8:19                           ` Jiri Pirko
  1 sibling, 1 reply; 87+ messages in thread
From: Jakub Kicinski @ 2023-01-10 20:05 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jiri Pirko, Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

On Mon, 9 Jan 2023 14:43:01 +0000 Kubalewski, Arkadiusz wrote:
> This is a simplified network switch board example.
> It has 2 synchronization channels, where each channel:
> - provides clk to 8 PHYs driven by separated MAC chips,
> - controls 2 DPLLs.
> 
> Basically only given FW has control over its PHYs, so also a control over it's
> MUX inputs.
> All external sources are shared between the channels.
> 
> This is why we believe it is not best idea to enclose multiple DPLLs with one
> object:
> - sources are shared even if DPLLs are not a single synchronizer chip,
> - control over specific MUX type input shall be controllable from different
> driver/firmware instances.
> 
> As we know the proposal of having multiple DPLLs in one object was a try to
> simplify currently implemented shared pins. We fully support idea of having
> interfaces as simple as possible, but at the same time they shall be flexible
> enough to serve many use cases.

I must be missing context from other discussions but what is this
proposal trying to solve? Well implemented shared pins is all we need.

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-10 20:05                         ` Jakub Kicinski
@ 2023-01-11  8:19                           ` Jiri Pirko
  2023-01-11 14:16                             ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2023-01-11  8:19 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Kubalewski, Arkadiusz, Maciek Machnikowski,
	'Vadim Fedorenko', 'Jonathan Lemon',
	'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

Tue, Jan 10, 2023 at 09:05:49PM CET, kuba@kernel.org wrote:
>On Mon, 9 Jan 2023 14:43:01 +0000 Kubalewski, Arkadiusz wrote:
>> This is a simplified network switch board example.
>> It has 2 synchronization channels, where each channel:
>> - provides clk to 8 PHYs driven by separated MAC chips,
>> - controls 2 DPLLs.
>> 
>> Basically only given FW has control over its PHYs, so also a control over it's
>> MUX inputs.
>> All external sources are shared between the channels.
>> 
>> This is why we believe it is not best idea to enclose multiple DPLLs with one
>> object:
>> - sources are shared even if DPLLs are not a single synchronizer chip,
>> - control over specific MUX type input shall be controllable from different
>> driver/firmware instances.
>> 
>> As we know the proposal of having multiple DPLLs in one object was a try to
>> simplify currently implemented shared pins. We fully support idea of having
>> interfaces as simple as possible, but at the same time they shall be flexible
>> enough to serve many use cases.
>
>I must be missing context from other discussions but what is this
>proposal trying to solve? Well implemented shared pins is all we need.

There is an entity containing the pins. The synchronizer chip. One
synchronizer chip contains 1-n DPLLs. The source pins are connected
to each DPLL (usually). What we missed in the original model was the
synchronizer entity. If we have it, we don't need any notion of somehow
floating pins as independent entities being attached to one or many
DPLL refcounted, etc. The synchronizer device holds them in
straightforward way.

Example of a synchronizer chip:
https://www.renesas.com/us/en/products/clocks-timing/jitter-attenuators-frequency-translation/8a34044-multichannel-dpll-dco-four-eight-channels#overview

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11  8:19                           ` Jiri Pirko
@ 2023-01-11 14:16                             ` Kubalewski, Arkadiusz
  2023-01-11 15:04                               ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-11 14:16 UTC (permalink / raw)
  To: Jiri Pirko, Jakub Kicinski
  Cc: Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, January 11, 2023 9:20 AM
>
>Tue, Jan 10, 2023 at 09:05:49PM CET, kuba@kernel.org wrote:
>>On Mon, 9 Jan 2023 14:43:01 +0000 Kubalewski, Arkadiusz wrote:
>>> This is a simplified network switch board example.
>>> It has 2 synchronization channels, where each channel:
>>> - provides clk to 8 PHYs driven by separated MAC chips,
>>> - controls 2 DPLLs.
>>>
>>> Basically only given FW has control over its PHYs, so also a control
>over it's
>>> MUX inputs.
>>> All external sources are shared between the channels.
>>>
>>> This is why we believe it is not best idea to enclose multiple DPLLs
>with one
>>> object:
>>> - sources are shared even if DPLLs are not a single synchronizer chip,
>>> - control over specific MUX type input shall be controllable from
>different
>>> driver/firmware instances.
>>>
>>> As we know the proposal of having multiple DPLLs in one object was a try
>to
>>> simplify currently implemented shared pins. We fully support idea of
>having
>>> interfaces as simple as possible, but at the same time they shall be
>flexible
>>> enough to serve many use cases.
>>
>>I must be missing context from other discussions but what is this
>>proposal trying to solve? Well implemented shared pins is all we need.
>
>There is an entity containing the pins. The synchronizer chip. One
>synchronizer chip contains 1-n DPLLs. The source pins are connected
>to each DPLL (usually). What we missed in the original model was the
>synchronizer entity. If we have it, we don't need any notion of somehow
>floating pins as independent entities being attached to one or many
>DPLL refcounted, etc. The synchronizer device holds them in
>straightforward way.
>
>Example of a synchronizer chip:
>https://www.renesas.com/us/en/products/clocks-timing/jitter-attenuators-
>frequency-translation/8a34044-multichannel-dpll-dco-four-eight-
>channels#overview

Not really, as explained above, multiple separated synchronizer chips can be
connected to the same external sources.
This is why I wrote this email, to better explain need for references between
DPLLs and shared pins.
Synchronizer chip object with multiple DPLLs would have sense if the pins would
only belong to that single chip, but this is not true.
As the pins are shared between multiple DPLLs (both inside 1 integrated circuit
and between multiple integrated circuits), all of them shall have current state
of the source or output.
Pins still need to be shared same as they would be inside of one synchronizer
chip.

BR,
Arkadiusz

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
       [not found]                             ` <645a5bfd-0092-2f39-0ff2-3ffb27ccf8fe@machnikowski.net>
@ 2023-01-11 14:17                               ` Kubalewski, Arkadiusz
  2023-01-11 14:40                                 ` Maciek Machnikowski
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-11 14:17 UTC (permalink / raw)
  To: Maciek Machnikowski, Jiri Pirko
  Cc: Jakub Kicinski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Maciek Machnikowski <maciek@machnikowski.net>
>Sent: Tuesday, January 10, 2023 3:59 PM
>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
><jiri@resnulli.us>
>
>On 1/10/2023 11:54 AM, Kubalewski, Arkadiusz wrote:
>>> From: Jiri Pirko <jiri@resnulli.us>
>>> Sent: Monday, January 9, 2023 5:30 PM
>>>>
>>>> Hi guys,
>>>>
>>>> We have been trying to figure out feasibility of new approach proposed
>on
>>> our
>>>> latest meeting - to have a single object which encapsulates multiple
>>> DPLLs.
>>>>
>>>> Please consider following example:
>>>>
>>>> Shared common inputs:
>>>> i0 - GPS  / external
>>>> i1 - SMA1 / external
>>>> i2 - SMA2 / external
>>>> i3 - MUX0 / clk recovered from PHY0.X driven by MAC0
>>>> i4 - MUX1 / clk recovered from PHY1.X driven by MAC1
>>>>
>>>> +---------------------------------------------------------+
>>>> | Channel A / FW0             +---+                       |
>>>> |                         i0--|   |                       |
>>>> |         +---+               |   |                       |
>>>> | PHY0.0--|   |           i1--| D |                       |
>>>> |         |   |               | P |                       |
>>>> | PHY0.1--| M |           i2--| L |   +---+   +--------+  |
>>>> |         | U |               | L |---|   |---| PHY0.0 |--|
>>>> | PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
>>>> |         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
>>>> | ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
>>>> |         |   | |+------+     +---+   | C |---| PHY0.2 |--|
>>>> | PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
>>>> |         +---+ |             |   |---|   |---| ...    |--|
>>>> |               |         i1--| D |   |   |   +--------+  |
>>>> |               |             | P |---|   |---| PHY0.7 |--|
>>>> |               |         i2--| L |   +---+   +--------+  |
>>>> |               |             | L |                       |
>>>> |               \---------i3--| 1 |                       |
>>>> |                +------+     |   |                       |
>>>> |                | MUX1 |-i4--|   |                       |
>>>> |                +------+     +---+                       |
>>>> +---------------------------------------------------------+
>>>> | Channel B / FW1             +---+                       |
>>>> |                         i0--|   |                       |
>>>> |                             |   |                       |
>>>> |                         i1--| D |                       |
>>>> |         +---+               | P |                       |
>>>> | PHY1.0--|   |           i2--| L |   +---+   +--------+  |
>>>> |         |   |  +------+     | L |---|   |---| PHY1.0 |--|
>>>> | PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
>>>> |         | U |  +------+     |   |---| M |---| PHY1.1 |--|
>>>> | PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
>>>> |         | 1 | |             +---+   | C |---| PHY1.2 |--|
>>>> | ...   --|   | |         i0--|   |   | 1 |   +--------+  |
>>>> |         |   | |             |   |---|   |---| ...    |--|
>>>> | PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
>>>> |         +---+ |             | P |---|   |---| PHY1.7 |--|
>>>> |               |         i2--| L |   +---+   +--------+  |
>>>> |               |+------+     | L |                       |
>>>> |               || MUX0 |-i3--| 1 |                       |
>>>> |               |+------+     |   |                       |
>>>> |               \---------i4--|   |                       |
>>>> |                             +---+                       |
>>>> +---------------------------------------------------------+
>>>
>>> What is "a channel" here? Are these 2 channels part of the same physival
>>> chip? Could you add the synchronizer chip/device entities to your
>drawing?
>>>
>>
>> No.
>> A "Synchronization Channel" on a switch would allow to separate groups
>> of physical ports. Each channel/group has own "Synchronizer Chip", which
>is
>> used to drive PHY clocks of that group.
>>
>> "Synchronizer chip" would be the 2 DPLLs on old draw, something like
>this:
>> +--------------------------------------------------------------+
>> | 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--|   |                                                |
>> |         +---+                                                |
>> +--------------------------------------------------------------+
>> Also, please keep in mind that is an example, there could be easily 4
>> (or more) channels wired similarly.
>>
>
>
>Hi,
>
>This model tries to put too much into the synchronizer subsystem. The
>synchronizer device should only model inputs, DPLLs and outputs.
>
>The PHY lane to Synchronizer input muxing should be done in the
>PHY/netdev subsystem. That's why I wanted to start with the full model
>to specifically address this topic.
>
>The netdev should have an assigned list of Synchronizer inputs that it
>can recover its SyncE clocks into. It can be done by having a connection
>between the synchronizer input object(s) and the netdev, just like the
>netdev is connected to PHC clocks in the PHC subsystem. This is the
>model I initially presented about a year ago for solving this specific
>issue.
>
>Analogically, the netdev will be connected to a given output, however
>changing anything in the physical clock configuration sounds dangerous.
>
>Does that sound reasonable?
>
>Regards
>Maciek

It sounds reasonable to some point.
You have mentioned list of Synchronizer inputs. If there is a list of inputs
it means it was created somewhere. I assume dpll subsystem? If so you would
like to export that list out of dpll subsystem, thus other entities would need
to find such list, then find particular source and somehow register with it.
All of this was proposed as part of netdev, I don't see any benefit in having
this parts separated from dpll, as only dpll would use it, right?
The same behavior is now provided by the MUX type pin, enclosed within dpll
subsystem.

BR,
Arkadiusz

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11 14:17                               ` Kubalewski, Arkadiusz
@ 2023-01-11 14:40                                 ` Maciek Machnikowski
  2023-01-11 15:30                                   ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Maciek Machnikowski @ 2023-01-11 14:40 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, Jiri Pirko
  Cc: Jakub Kicinski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk



On 1/11/2023 3:17 PM, Kubalewski, Arkadiusz wrote:
>> From: Maciek Machnikowski <maciek@machnikowski.net>
>> Sent: Tuesday, January 10, 2023 3:59 PM
>> To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
>> <jiri@resnulli.us>
>>
>> On 1/10/2023 11:54 AM, Kubalewski, Arkadiusz wrote:
>>>> From: Jiri Pirko <jiri@resnulli.us>
>>>> Sent: Monday, January 9, 2023 5:30 PM
>>>>>
>>>>> Hi guys,
>>>>>
>>>>> We have been trying to figure out feasibility of new approach proposed
>> on
>>>> our
>>>>> latest meeting - to have a single object which encapsulates multiple
>>>> DPLLs.
>>>>>
>>>>> Please consider following example:
>>>>>
>>>>> Shared common inputs:
>>>>> i0 - GPS  / external
>>>>> i1 - SMA1 / external
>>>>> i2 - SMA2 / external
>>>>> i3 - MUX0 / clk recovered from PHY0.X driven by MAC0
>>>>> i4 - MUX1 / clk recovered from PHY1.X driven by MAC1
>>>>>
>>>>> +---------------------------------------------------------+
>>>>> | Channel A / FW0             +---+                       |
>>>>> |                         i0--|   |                       |
>>>>> |         +---+               |   |                       |
>>>>> | PHY0.0--|   |           i1--| D |                       |
>>>>> |         |   |               | P |                       |
>>>>> | PHY0.1--| M |           i2--| L |   +---+   +--------+  |
>>>>> |         | U |               | L |---|   |---| PHY0.0 |--|
>>>>> | PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
>>>>> |         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
>>>>> | ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
>>>>> |         |   | |+------+     +---+   | C |---| PHY0.2 |--|
>>>>> | PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
>>>>> |         +---+ |             |   |---|   |---| ...    |--|
>>>>> |               |         i1--| D |   |   |   +--------+  |
>>>>> |               |             | P |---|   |---| PHY0.7 |--|
>>>>> |               |         i2--| L |   +---+   +--------+  |
>>>>> |               |             | L |                       |
>>>>> |               \---------i3--| 1 |                       |
>>>>> |                +------+     |   |                       |
>>>>> |                | MUX1 |-i4--|   |                       |
>>>>> |                +------+     +---+                       |
>>>>> +---------------------------------------------------------+
>>>>> | Channel B / FW1             +---+                       |
>>>>> |                         i0--|   |                       |
>>>>> |                             |   |                       |
>>>>> |                         i1--| D |                       |
>>>>> |         +---+               | P |                       |
>>>>> | PHY1.0--|   |           i2--| L |   +---+   +--------+  |
>>>>> |         |   |  +------+     | L |---|   |---| PHY1.0 |--|
>>>>> | PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
>>>>> |         | U |  +------+     |   |---| M |---| PHY1.1 |--|
>>>>> | PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
>>>>> |         | 1 | |             +---+   | C |---| PHY1.2 |--|
>>>>> | ...   --|   | |         i0--|   |   | 1 |   +--------+  |
>>>>> |         |   | |             |   |---|   |---| ...    |--|
>>>>> | PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
>>>>> |         +---+ |             | P |---|   |---| PHY1.7 |--|
>>>>> |               |         i2--| L |   +---+   +--------+  |
>>>>> |               |+------+     | L |                       |
>>>>> |               || MUX0 |-i3--| 1 |                       |
>>>>> |               |+------+     |   |                       |
>>>>> |               \---------i4--|   |                       |
>>>>> |                             +---+                       |
>>>>> +---------------------------------------------------------+
>>>>
>>>> What is "a channel" here? Are these 2 channels part of the same physival
>>>> chip? Could you add the synchronizer chip/device entities to your
>> drawing?
>>>>
>>>
>>> No.
>>> A "Synchronization Channel" on a switch would allow to separate groups
>>> of physical ports. Each channel/group has own "Synchronizer Chip", which
>> is
>>> used to drive PHY clocks of that group.
>>>
>>> "Synchronizer chip" would be the 2 DPLLs on old draw, something like
>> this:
>>> +--------------------------------------------------------------+
>>> | 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--|   |                                                |
>>> |         +---+                                                |
>>> +--------------------------------------------------------------+
>>> Also, please keep in mind that is an example, there could be easily 4
>>> (or more) channels wired similarly.
>>>
>>
>>
>> Hi,
>>
>> This model tries to put too much into the synchronizer subsystem. The
>> synchronizer device should only model inputs, DPLLs and outputs.
>>
>> The PHY lane to Synchronizer input muxing should be done in the
>> PHY/netdev subsystem. That's why I wanted to start with the full model
>> to specifically address this topic.
>>
>> The netdev should have an assigned list of Synchronizer inputs that it
>> can recover its SyncE clocks into. It can be done by having a connection
>> between the synchronizer input object(s) and the netdev, just like the
>> netdev is connected to PHC clocks in the PHC subsystem. This is the
>> model I initially presented about a year ago for solving this specific
>> issue.
>>
>> Analogically, the netdev will be connected to a given output, however
>> changing anything in the physical clock configuration sounds dangerous.
>>
>> Does that sound reasonable?
>>
>> Regards
>> Maciek
> 
> It sounds reasonable to some point.
> You have mentioned list of Synchronizer inputs. If there is a list of inputs
> it means it was created somewhere. I assume dpll subsystem? If so you would
> like to export that list out of dpll subsystem, thus other entities would need
> to find such list, then find particular source and somehow register with it.
> All of this was proposed as part of netdev, I don't see any benefit in having
> this parts separated from dpll, as only dpll would use it, right?
> The same behavior is now provided by the MUX type pin, enclosed within dpll
> subsystem.
> 
> BR,
> Arkadiusz

The synchronizer object should expose the list of inputs that represent
possible sources of a given chip. The list will be the same for all
DPLLs used by the same device, so it can be a single set of sources
linked to multiple DPLLs inside the package. A netdev can then point to
a given input of a synchronizer that it's connected to.
The phy lane->recovered clock (or directly a synchronizer input) muxing
should stay in the netdev subsystem, or in the PHY driver.

The reason, and benefit, of such split is when you create a board with a
netdev X and a synchronizer Y that is not instantiated by the same
driver. In this scenario you'd get the ice driver to instantiate
connections and the DPLL vendor's driver for the synchronizer. In such
case the netdev driver will simply send a netlink message to the
input/source with a requested configuration, such as expected frequency,
and everything from this point can be handled by a completely different
driver creating clean and logical split.

If we mix the phy lanes into the DPLL subsystem it'll get very
challenging to add PHY lanes to the existing synchronizer exposed by a
different driver.

Exporting and link between the synchronizer and the netdev is still a
must no matter which way we go. And IMO it's best to link netdev to
synchronizer sources, as that's the most natural way.

Thanks,
Maciek

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11 14:16                             ` Kubalewski, Arkadiusz
@ 2023-01-11 15:04                               ` Jiri Pirko
  2023-01-11 15:30                                 ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2023-01-11 15:04 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

Wed, Jan 11, 2023 at 03:16:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, January 11, 2023 9:20 AM
>>
>>Tue, Jan 10, 2023 at 09:05:49PM CET, kuba@kernel.org wrote:
>>>On Mon, 9 Jan 2023 14:43:01 +0000 Kubalewski, Arkadiusz wrote:
>>>> This is a simplified network switch board example.
>>>> It has 2 synchronization channels, where each channel:
>>>> - provides clk to 8 PHYs driven by separated MAC chips,
>>>> - controls 2 DPLLs.
>>>>
>>>> Basically only given FW has control over its PHYs, so also a control
>>over it's
>>>> MUX inputs.
>>>> All external sources are shared between the channels.
>>>>
>>>> This is why we believe it is not best idea to enclose multiple DPLLs
>>with one
>>>> object:
>>>> - sources are shared even if DPLLs are not a single synchronizer chip,
>>>> - control over specific MUX type input shall be controllable from
>>different
>>>> driver/firmware instances.
>>>>
>>>> As we know the proposal of having multiple DPLLs in one object was a try
>>to
>>>> simplify currently implemented shared pins. We fully support idea of
>>having
>>>> interfaces as simple as possible, but at the same time they shall be
>>flexible
>>>> enough to serve many use cases.
>>>
>>>I must be missing context from other discussions but what is this
>>>proposal trying to solve? Well implemented shared pins is all we need.
>>
>>There is an entity containing the pins. The synchronizer chip. One
>>synchronizer chip contains 1-n DPLLs. The source pins are connected
>>to each DPLL (usually). What we missed in the original model was the
>>synchronizer entity. If we have it, we don't need any notion of somehow
>>floating pins as independent entities being attached to one or many
>>DPLL refcounted, etc. The synchronizer device holds them in
>>straightforward way.
>>
>>Example of a synchronizer chip:
>>https://www.renesas.com/us/en/products/clocks-timing/jitter-attenuators-
>>frequency-translation/8a34044-multichannel-dpll-dco-four-eight-
>>channels#overview
>
>Not really, as explained above, multiple separated synchronizer chips can be
>connected to the same external sources.
>This is why I wrote this email, to better explain need for references between
>DPLLs and shared pins.
>Synchronizer chip object with multiple DPLLs would have sense if the pins would
>only belong to that single chip, but this is not true.

I don't understand how it is physically possible that 2 pins belong to 2
chips. Could you draw this to me?


>As the pins are shared between multiple DPLLs (both inside 1 integrated circuit
>and between multiple integrated circuits), all of them shall have current state
>of the source or output.
>Pins still need to be shared same as they would be inside of one synchronizer
>chip.

Do I understand correctly that you connect one synchronizer output to
the input of the second synchronizer chip?

>
>BR,
>Arkadiusz

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11 14:40                                 ` Maciek Machnikowski
@ 2023-01-11 15:30                                   ` Kubalewski, Arkadiusz
  2023-01-11 15:54                                     ` Maciek Machnikowski
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-11 15:30 UTC (permalink / raw)
  To: Maciek Machnikowski, Jiri Pirko
  Cc: Jakub Kicinski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Maciek Machnikowski <maciek@machnikowski.net>
>Sent: Wednesday, January 11, 2023 3:40 PM
>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
><jiri@resnulli.us>
>
>
>On 1/11/2023 3:17 PM, Kubalewski, Arkadiusz wrote:
>>> From: Maciek Machnikowski <maciek@machnikowski.net>
>>> Sent: Tuesday, January 10, 2023 3:59 PM
>>> To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
>>> <jiri@resnulli.us>
>>>
>>> On 1/10/2023 11:54 AM, Kubalewski, Arkadiusz wrote:
>>>>> From: Jiri Pirko <jiri@resnulli.us>
>>>>> Sent: Monday, January 9, 2023 5:30 PM
>>>>>>
>>>>>> Hi guys,
>>>>>>
>>>>>> We have been trying to figure out feasibility of new approach
>proposed
>>> on
>>>>> our
>>>>>> latest meeting - to have a single object which encapsulates multiple
>>>>> DPLLs.
>>>>>>
>>>>>> Please consider following example:
>>>>>>
>>>>>> Shared common inputs:
>>>>>> i0 - GPS  / external
>>>>>> i1 - SMA1 / external
>>>>>> i2 - SMA2 / external
>>>>>> i3 - MUX0 / clk recovered from PHY0.X driven by MAC0
>>>>>> i4 - MUX1 / clk recovered from PHY1.X driven by MAC1
>>>>>>
>>>>>> +---------------------------------------------------------+
>>>>>> | Channel A / FW0             +---+                       |
>>>>>> |                         i0--|   |                       |
>>>>>> |         +---+               |   |                       |
>>>>>> | PHY0.0--|   |           i1--| D |                       |
>>>>>> |         |   |               | P |                       |
>>>>>> | PHY0.1--| M |           i2--| L |   +---+   +--------+  |
>>>>>> |         | U |               | L |---|   |---| PHY0.0 |--|
>>>>>> | PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
>>>>>> |         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
>>>>>> | ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
>>>>>> |         |   | |+------+     +---+   | C |---| PHY0.2 |--|
>>>>>> | PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
>>>>>> |         +---+ |             |   |---|   |---| ...    |--|
>>>>>> |               |         i1--| D |   |   |   +--------+  |
>>>>>> |               |             | P |---|   |---| PHY0.7 |--|
>>>>>> |               |         i2--| L |   +---+   +--------+  |
>>>>>> |               |             | L |                       |
>>>>>> |               \---------i3--| 1 |                       |
>>>>>> |                +------+     |   |                       |
>>>>>> |                | MUX1 |-i4--|   |                       |
>>>>>> |                +------+     +---+                       |
>>>>>> +---------------------------------------------------------+
>>>>>> | Channel B / FW1             +---+                       |
>>>>>> |                         i0--|   |                       |
>>>>>> |                             |   |                       |
>>>>>> |                         i1--| D |                       |
>>>>>> |         +---+               | P |                       |
>>>>>> | PHY1.0--|   |           i2--| L |   +---+   +--------+  |
>>>>>> |         |   |  +------+     | L |---|   |---| PHY1.0 |--|
>>>>>> | PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
>>>>>> |         | U |  +------+     |   |---| M |---| PHY1.1 |--|
>>>>>> | PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
>>>>>> |         | 1 | |             +---+   | C |---| PHY1.2 |--|
>>>>>> | ...   --|   | |         i0--|   |   | 1 |   +--------+  |
>>>>>> |         |   | |             |   |---|   |---| ...    |--|
>>>>>> | PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
>>>>>> |         +---+ |             | P |---|   |---| PHY1.7 |--|
>>>>>> |               |         i2--| L |   +---+   +--------+  |
>>>>>> |               |+------+     | L |                       |
>>>>>> |               || MUX0 |-i3--| 1 |                       |
>>>>>> |               |+------+     |   |                       |
>>>>>> |               \---------i4--|   |                       |
>>>>>> |                             +---+                       |
>>>>>> +---------------------------------------------------------+
>>>>>
>>>>> What is "a channel" here? Are these 2 channels part of the same
>physival
>>>>> chip? Could you add the synchronizer chip/device entities to your
>>> drawing?
>>>>>
>>>>
>>>> No.
>>>> A "Synchronization Channel" on a switch would allow to separate groups
>>>> of physical ports. Each channel/group has own "Synchronizer Chip",
>which
>>> is
>>>> used to drive PHY clocks of that group.
>>>>
>>>> "Synchronizer chip" would be the 2 DPLLs on old draw, something like
>>> this:
>>>> +--------------------------------------------------------------+
>>>> | 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--|   |                                                |
>>>> |         +---+                                                |
>>>> +--------------------------------------------------------------+
>>>> Also, please keep in mind that is an example, there could be easily 4
>>>> (or more) channels wired similarly.
>>>>
>>>
>>>
>>> Hi,
>>>
>>> This model tries to put too much into the synchronizer subsystem. The
>>> synchronizer device should only model inputs, DPLLs and outputs.
>>>
>>> The PHY lane to Synchronizer input muxing should be done in the
>>> PHY/netdev subsystem. That's why I wanted to start with the full model
>>> to specifically address this topic.
>>>
>>> The netdev should have an assigned list of Synchronizer inputs that it
>>> can recover its SyncE clocks into. It can be done by having a connection
>>> between the synchronizer input object(s) and the netdev, just like the
>>> netdev is connected to PHC clocks in the PHC subsystem. This is the
>>> model I initially presented about a year ago for solving this specific
>>> issue.
>>>
>>> Analogically, the netdev will be connected to a given output, however
>>> changing anything in the physical clock configuration sounds dangerous.
>>>
>>> Does that sound reasonable?
>>>
>>> Regards
>>> Maciek
>>
>> It sounds reasonable to some point.
>> You have mentioned list of Synchronizer inputs. If there is a list of
>inputs
>> it means it was created somewhere. I assume dpll subsystem? If so you
>would
>> like to export that list out of dpll subsystem, thus other entities would
>need
>> to find such list, then find particular source and somehow register with
>it.
>> All of this was proposed as part of netdev, I don't see any benefit in
>having
>> this parts separated from dpll, as only dpll would use it, right?
>> The same behavior is now provided by the MUX type pin, enclosed within
>dpll
>> subsystem.
>>
>> BR,
>> Arkadiusz
>
>The synchronizer object should expose the list of inputs that represent
>possible sources of a given chip. The list will be the same for all
>DPLLs used by the same device, so it can be a single set of sources
>linked to multiple DPLLs inside the package. A netdev can then point to
>a given input of a synchronizer that it's connected to.
>The phy lane->recovered clock (or directly a synchronizer input) muxing
>should stay in the netdev subsystem, or in the PHY driver.
>
>The reason, and benefit, of such split is when you create a board with a
>netdev X and a synchronizer Y that is not instantiated by the same
>driver. In this scenario you'd get the ice driver to instantiate
>connections and the DPLL vendor's driver for the synchronizer. In such
>case the netdev driver will simply send a netlink message to the
>input/source with a requested configuration, such as expected frequency,
>and everything from this point can be handled by a completely different
>driver creating clean and logical split.
>
>If we mix the phy lanes into the DPLL subsystem it'll get very
>challenging to add PHY lanes to the existing synchronizer exposed by a
>different driver.

This is possible right now:
1. obtain a dpll object:
struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
						enum dpll_type type, u8 idx);
2. register new pin with muxed type 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);

To find dpll driver must know clock_id, type of dpll and its index given
when dpll was registered.
To register a pin, parent_pin_description of MUX type pin given on registering
it with dpll device.

>
>Exporting and link between the synchronizer and the netdev is still a
>must no matter which way we go. And IMO it's best to link netdev to
>synchronizer sources, as that's the most natural way.
>

The link is now just information for userspace Linux network interface index
in DPLLA_PIN_NETIFINDEX attribute.

BR,
Arkadiusz

>Thanks,
>Maciek

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11 15:04                               ` Jiri Pirko
@ 2023-01-11 15:30                                 ` Kubalewski, Arkadiusz
  2023-01-11 16:14                                   ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-11 15:30 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, January 11, 2023 4:05 PM
>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>
>Wed, Jan 11, 2023 at 03:16:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, January 11, 2023 9:20 AM
>>>
>>>Tue, Jan 10, 2023 at 09:05:49PM CET, kuba@kernel.org wrote:
>>>>On Mon, 9 Jan 2023 14:43:01 +0000 Kubalewski, Arkadiusz wrote:
>>>>> This is a simplified network switch board example.
>>>>> It has 2 synchronization channels, where each channel:
>>>>> - provides clk to 8 PHYs driven by separated MAC chips,
>>>>> - controls 2 DPLLs.
>>>>>
>>>>> Basically only given FW has control over its PHYs, so also a control
>>>over it's
>>>>> MUX inputs.
>>>>> All external sources are shared between the channels.
>>>>>
>>>>> This is why we believe it is not best idea to enclose multiple DPLLs
>>>with one
>>>>> object:
>>>>> - sources are shared even if DPLLs are not a single synchronizer chip,
>>>>> - control over specific MUX type input shall be controllable from
>>>different
>>>>> driver/firmware instances.
>>>>>
>>>>> As we know the proposal of having multiple DPLLs in one object was a
>try
>>>to
>>>>> simplify currently implemented shared pins. We fully support idea of
>>>having
>>>>> interfaces as simple as possible, but at the same time they shall be
>>>flexible
>>>>> enough to serve many use cases.
>>>>
>>>>I must be missing context from other discussions but what is this
>>>>proposal trying to solve? Well implemented shared pins is all we need.
>>>
>>>There is an entity containing the pins. The synchronizer chip. One
>>>synchronizer chip contains 1-n DPLLs. The source pins are connected
>>>to each DPLL (usually). What we missed in the original model was the
>>>synchronizer entity. If we have it, we don't need any notion of somehow
>>>floating pins as independent entities being attached to one or many
>>>DPLL refcounted, etc. The synchronizer device holds them in
>>>straightforward way.
>>>
>>>Example of a synchronizer chip:
>>>https://www.renesas.com/us/en/products/clocks-timing/jitter-attenuators-
>>>frequency-translation/8a34044-multichannel-dpll-dco-four-eight-
>>>channels#overview
>>
>>Not really, as explained above, multiple separated synchronizer chips can
>be
>>connected to the same external sources.
>>This is why I wrote this email, to better explain need for references
>between
>>DPLLs and shared pins.
>>Synchronizer chip object with multiple DPLLs would have sense if the pins
>would
>>only belong to that single chip, but this is not true.
>
>I don't understand how it is physically possible that 2 pins belong to 2
>chips. Could you draw this to me?
>

Well, sure, I was hoping this is clear, without extra connections on the draw:
+----------+                 
|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--|   |                                                       |
|         +---+                                                       |
+---------------------------------------------------------------------+

>
>>As the pins are shared between multiple DPLLs (both inside 1 integrated
>circuit
>>and between multiple integrated circuits), all of them shall have current
>state
>>of the source or output.
>>Pins still need to be shared same as they would be inside of one
>synchronizer
>>chip.
>
>Do I understand correctly that you connect one synchronizer output to
>the input of the second synchronizer chip?

No, I don't recall such use case. At least nothing that needs to exposed
in the DPLL subsystem itself.

BR,
Arkadiusz

>
>>
>>BR,
>>Arkadiusz

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11 15:30                                   ` Kubalewski, Arkadiusz
@ 2023-01-11 15:54                                     ` Maciek Machnikowski
  2023-01-11 16:27                                       ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Maciek Machnikowski @ 2023-01-11 15:54 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz, Jiri Pirko
  Cc: Jakub Kicinski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk



On 1/11/2023 4:30 PM, Kubalewski, Arkadiusz wrote:
>> From: Maciek Machnikowski <maciek@machnikowski.net>
>> Sent: Wednesday, January 11, 2023 3:40 PM
>> To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
>> <jiri@resnulli.us>
>>
>>
>> On 1/11/2023 3:17 PM, Kubalewski, Arkadiusz wrote:
>>>> From: Maciek Machnikowski <maciek@machnikowski.net>
>>>> Sent: Tuesday, January 10, 2023 3:59 PM
>>>> To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
>>>> <jiri@resnulli.us>
>>>>
>>>> On 1/10/2023 11:54 AM, Kubalewski, Arkadiusz wrote:
>>>>>> From: Jiri Pirko <jiri@resnulli.us>
>>>>>> Sent: Monday, January 9, 2023 5:30 PM
>>>>>>>
>>>>>>> Hi guys,
>>>>>>>
>>>>>>> We have been trying to figure out feasibility of new approach
>> proposed
>>>> on
>>>>>> our
>>>>>>> latest meeting - to have a single object which encapsulates multiple
>>>>>> DPLLs.
>>>>>>>
>>>>>>> Please consider following example:
>>>>>>>
>>>>>>> Shared common inputs:
>>>>>>> i0 - GPS  / external
>>>>>>> i1 - SMA1 / external
>>>>>>> i2 - SMA2 / external
>>>>>>> i3 - MUX0 / clk recovered from PHY0.X driven by MAC0
>>>>>>> i4 - MUX1 / clk recovered from PHY1.X driven by MAC1
>>>>>>>
>>>>>>> +---------------------------------------------------------+
>>>>>>> | Channel A / FW0             +---+                       |
>>>>>>> |                         i0--|   |                       |
>>>>>>> |         +---+               |   |                       |
>>>>>>> | PHY0.0--|   |           i1--| D |                       |
>>>>>>> |         |   |               | P |                       |
>>>>>>> | PHY0.1--| M |           i2--| L |   +---+   +--------+  |
>>>>>>> |         | U |               | L |---|   |---| PHY0.0 |--|
>>>>>>> | PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
>>>>>>> |         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
>>>>>>> | ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
>>>>>>> |         |   | |+------+     +---+   | C |---| PHY0.2 |--|
>>>>>>> | PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
>>>>>>> |         +---+ |             |   |---|   |---| ...    |--|
>>>>>>> |               |         i1--| D |   |   |   +--------+  |
>>>>>>> |               |             | P |---|   |---| PHY0.7 |--|
>>>>>>> |               |         i2--| L |   +---+   +--------+  |
>>>>>>> |               |             | L |                       |
>>>>>>> |               \---------i3--| 1 |                       |
>>>>>>> |                +------+     |   |                       |
>>>>>>> |                | MUX1 |-i4--|   |                       |
>>>>>>> |                +------+     +---+                       |
>>>>>>> +---------------------------------------------------------+
>>>>>>> | Channel B / FW1             +---+                       |
>>>>>>> |                         i0--|   |                       |
>>>>>>> |                             |   |                       |
>>>>>>> |                         i1--| D |                       |
>>>>>>> |         +---+               | P |                       |
>>>>>>> | PHY1.0--|   |           i2--| L |   +---+   +--------+  |
>>>>>>> |         |   |  +------+     | L |---|   |---| PHY1.0 |--|
>>>>>>> | PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
>>>>>>> |         | U |  +------+     |   |---| M |---| PHY1.1 |--|
>>>>>>> | PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
>>>>>>> |         | 1 | |             +---+   | C |---| PHY1.2 |--|
>>>>>>> | ...   --|   | |         i0--|   |   | 1 |   +--------+  |
>>>>>>> |         |   | |             |   |---|   |---| ...    |--|
>>>>>>> | PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
>>>>>>> |         +---+ |             | P |---|   |---| PHY1.7 |--|
>>>>>>> |               |         i2--| L |   +---+   +--------+  |
>>>>>>> |               |+------+     | L |                       |
>>>>>>> |               || MUX0 |-i3--| 1 |                       |
>>>>>>> |               |+------+     |   |                       |
>>>>>>> |               \---------i4--|   |                       |
>>>>>>> |                             +---+                       |
>>>>>>> +---------------------------------------------------------+
>>>>>>
>>>>>> What is "a channel" here? Are these 2 channels part of the same
>> physival
>>>>>> chip? Could you add the synchronizer chip/device entities to your
>>>> drawing?
>>>>>>
>>>>>
>>>>> No.
>>>>> A "Synchronization Channel" on a switch would allow to separate groups
>>>>> of physical ports. Each channel/group has own "Synchronizer Chip",
>> which
>>>> is
>>>>> used to drive PHY clocks of that group.
>>>>>
>>>>> "Synchronizer chip" would be the 2 DPLLs on old draw, something like
>>>> this:
>>>>> +--------------------------------------------------------------+
>>>>> | 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--|   |                                                |
>>>>> |         +---+                                                |
>>>>> +--------------------------------------------------------------+
>>>>> Also, please keep in mind that is an example, there could be easily 4
>>>>> (or more) channels wired similarly.
>>>>>
>>>>
>>>>
>>>> Hi,
>>>>
>>>> This model tries to put too much into the synchronizer subsystem. The
>>>> synchronizer device should only model inputs, DPLLs and outputs.
>>>>
>>>> The PHY lane to Synchronizer input muxing should be done in the
>>>> PHY/netdev subsystem. That's why I wanted to start with the full model
>>>> to specifically address this topic.
>>>>
>>>> The netdev should have an assigned list of Synchronizer inputs that it
>>>> can recover its SyncE clocks into. It can be done by having a connection
>>>> between the synchronizer input object(s) and the netdev, just like the
>>>> netdev is connected to PHC clocks in the PHC subsystem. This is the
>>>> model I initially presented about a year ago for solving this specific
>>>> issue.
>>>>
>>>> Analogically, the netdev will be connected to a given output, however
>>>> changing anything in the physical clock configuration sounds dangerous.
>>>>
>>>> Does that sound reasonable?
>>>>
>>>> Regards
>>>> Maciek
>>>
>>> It sounds reasonable to some point.
>>> You have mentioned list of Synchronizer inputs. If there is a list of
>> inputs
>>> it means it was created somewhere. I assume dpll subsystem? If so you
>> would
>>> like to export that list out of dpll subsystem, thus other entities would
>> need
>>> to find such list, then find particular source and somehow register with
>> it.
>>> All of this was proposed as part of netdev, I don't see any benefit in
>> having
>>> this parts separated from dpll, as only dpll would use it, right?
>>> The same behavior is now provided by the MUX type pin, enclosed within
>> dpll
>>> subsystem.
>>>
>>> BR,
>>> Arkadiusz
>>
>> The synchronizer object should expose the list of inputs that represent
>> possible sources of a given chip. The list will be the same for all
>> DPLLs used by the same device, so it can be a single set of sources
>> linked to multiple DPLLs inside the package. A netdev can then point to
>> a given input of a synchronizer that it's connected to.
>> The phy lane->recovered clock (or directly a synchronizer input) muxing
>> should stay in the netdev subsystem, or in the PHY driver.
>>
>> The reason, and benefit, of such split is when you create a board with a
>> netdev X and a synchronizer Y that is not instantiated by the same
>> driver. In this scenario you'd get the ice driver to instantiate
>> connections and the DPLL vendor's driver for the synchronizer. In such
>> case the netdev driver will simply send a netlink message to the
>> input/source with a requested configuration, such as expected frequency,
>> and everything from this point can be handled by a completely different
>> driver creating clean and logical split.
>>
>> If we mix the phy lanes into the DPLL subsystem it'll get very
>> challenging to add PHY lanes to the existing synchronizer exposed by a
>> different driver.
> 
> This is possible right now:
> 1. obtain a dpll object:
> struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
> 						enum dpll_type type, u8 idx);
> 2. register new pin with muxed type 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);
> 
> To find dpll driver must know clock_id, type of dpll and its index given
> when dpll was registered.
> To register a pin, parent_pin_description of MUX type pin given on registering
> it with dpll device.

That would mean you need to repeat this process for all the DPLLs that
are co-packaged in a single synchronizer. Some chips have up to 8 DPLLs,
so you'd need to register number of phy lanes x number of DPLL times,
say 8x8 = 64 times - that's simply too messy in the long term.

>> Exporting and link between the synchronizer and the netdev is still a
>> must no matter which way we go. And IMO it's best to link netdev to
>> synchronizer sources, as that's the most natural way.
>>
> 
> The link is now just information for userspace Linux network interface index
> in DPLLA_PIN_NETIFINDEX attribute.

That's not the right way. We need to know:
- which DPLL pins/sources are driven by which netdev (to be able to
  identify a source of frequency that is currently driving a given DPLL)
- which DPLL generates a frequency for a given netdev (to know which
  DPLL to check for a specific netdev)

So we need to have 2 connections.

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11 15:30                                 ` Kubalewski, Arkadiusz
@ 2023-01-11 16:14                                   ` Jiri Pirko
  2023-01-12 12:15                                     ` Kubalewski, Arkadiusz
  0 siblings, 1 reply; 87+ messages in thread
From: Jiri Pirko @ 2023-01-11 16:14 UTC (permalink / raw)
  To: Kubalewski, Arkadiusz
  Cc: Jakub Kicinski, Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

Wed, Jan 11, 2023 at 04:30:44PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, January 11, 2023 4:05 PM
>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>
>>Wed, Jan 11, 2023 at 03:16:59PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Wednesday, January 11, 2023 9:20 AM
>>>>
>>>>Tue, Jan 10, 2023 at 09:05:49PM CET, kuba@kernel.org wrote:
>>>>>On Mon, 9 Jan 2023 14:43:01 +0000 Kubalewski, Arkadiusz wrote:
>>>>>> This is a simplified network switch board example.
>>>>>> It has 2 synchronization channels, where each channel:
>>>>>> - provides clk to 8 PHYs driven by separated MAC chips,
>>>>>> - controls 2 DPLLs.
>>>>>>
>>>>>> Basically only given FW has control over its PHYs, so also a control
>>>>over it's
>>>>>> MUX inputs.
>>>>>> All external sources are shared between the channels.
>>>>>>
>>>>>> This is why we believe it is not best idea to enclose multiple DPLLs
>>>>with one
>>>>>> object:
>>>>>> - sources are shared even if DPLLs are not a single synchronizer chip,
>>>>>> - control over specific MUX type input shall be controllable from
>>>>different
>>>>>> driver/firmware instances.
>>>>>>
>>>>>> As we know the proposal of having multiple DPLLs in one object was a
>>try
>>>>to
>>>>>> simplify currently implemented shared pins. We fully support idea of
>>>>having
>>>>>> interfaces as simple as possible, but at the same time they shall be
>>>>flexible
>>>>>> enough to serve many use cases.
>>>>>
>>>>>I must be missing context from other discussions but what is this
>>>>>proposal trying to solve? Well implemented shared pins is all we need.
>>>>
>>>>There is an entity containing the pins. The synchronizer chip. One
>>>>synchronizer chip contains 1-n DPLLs. The source pins are connected
>>>>to each DPLL (usually). What we missed in the original model was the
>>>>synchronizer entity. If we have it, we don't need any notion of somehow
>>>>floating pins as independent entities being attached to one or many
>>>>DPLL refcounted, etc. The synchronizer device holds them in
>>>>straightforward way.
>>>>
>>>>Example of a synchronizer chip:
>>>>https://www.renesas.com/us/en/products/clocks-timing/jitter-attenuators-
>>>>frequency-translation/8a34044-multichannel-dpll-dco-four-eight-
>>>>channels#overview
>>>
>>>Not really, as explained above, multiple separated synchronizer chips can
>>be
>>>connected to the same external sources.
>>>This is why I wrote this email, to better explain need for references
>>between
>>>DPLLs and shared pins.
>>>Synchronizer chip object with multiple DPLLs would have sense if the pins
>>would
>>>only belong to that single chip, but this is not true.
>>
>>I don't understand how it is physically possible that 2 pins belong to 2
>>chips. Could you draw this to me?
>>
>
>Well, sure, I was hoping this is clear, without extra connections on the draw:

Okay, now I understand. It is not a shared pin but shared source for 2
pins.


>+----------+                 
>|i0 - GPS  |--------------\
>+----------+              |
>+----------+              |
>|i1 - SMA1 |------------\ |
>+----------+            | |
>+----------+            | |
>|i2 - SMA2 |----------\ | |
>+----------+          | | |
>                      | | |
>+---------------------|-|-|-------------------------------------------+
>| Channel A / FW0     | | |     +-------------+   +---+   +--------+  |
>|                     | | |-i0--|Synchronizer0|---|   |---| PHY0.0 |--|

One pin here               ^^^

>|         +---+       | | |     |             |   |   |   +--------+  |
>| 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 |--|

And second pin here        ^^^

There are 2 separate pins. Sure, they need to have the same config as
they are connected to the same external entity (GPS, SMA1, SMA2).

Perhaps we need to have a board description using dts to draw this
picture so the drivers can use this schema in order to properly
configure this?

My point is, you are trying to hardcode the board geometry in the
driver. Is that correct?


>|         +---+  | |  | |       |             |   |   |   +--------+  |
>| 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--|   |                                                       |
>|         +---+                                                       |
>+---------------------------------------------------------------------+
>
>>
>>>As the pins are shared between multiple DPLLs (both inside 1 integrated
>>circuit
>>>and between multiple integrated circuits), all of them shall have current
>>state
>>>of the source or output.
>>>Pins still need to be shared same as they would be inside of one
>>synchronizer
>>>chip.
>>
>>Do I understand correctly that you connect one synchronizer output to
>>the input of the second synchronizer chip?
>
>No, I don't recall such use case. At least nothing that needs to exposed
>in the DPLL subsystem itself.
>
>BR,
>Arkadiusz
>
>>
>>>
>>>BR,
>>>Arkadiusz

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11 15:54                                     ` Maciek Machnikowski
@ 2023-01-11 16:27                                       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-11 16:27 UTC (permalink / raw)
  To: Maciek Machnikowski, Jiri Pirko
  Cc: Jakub Kicinski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk



BR, Arkadiusz

>-----Original Message-----
>From: Maciek Machnikowski <maciek@machnikowski.net>
>Sent: Wednesday, January 11, 2023 4:54 PM
>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
><jiri@resnulli.us>
>Cc: Jakub Kicinski <kuba@kernel.org>; 'Vadim Fedorenko'
><vfedorenko@novek.ru>; '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
>Subject: Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
>
>
>
>On 1/11/2023 4:30 PM, Kubalewski, Arkadiusz wrote:
>>> From: Maciek Machnikowski <maciek@machnikowski.net>
>>> Sent: Wednesday, January 11, 2023 3:40 PM
>>> To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
>>> <jiri@resnulli.us>
>>>
>>>
>>> On 1/11/2023 3:17 PM, Kubalewski, Arkadiusz wrote:
>>>>> From: Maciek Machnikowski <maciek@machnikowski.net>
>>>>> Sent: Tuesday, January 10, 2023 3:59 PM
>>>>> To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>; Jiri Pirko
>>>>> <jiri@resnulli.us>
>>>>>
>>>>> On 1/10/2023 11:54 AM, Kubalewski, Arkadiusz wrote:
>>>>>>> From: Jiri Pirko <jiri@resnulli.us>
>>>>>>> Sent: Monday, January 9, 2023 5:30 PM
>>>>>>>>
>>>>>>>> Hi guys,
>>>>>>>>
>>>>>>>> We have been trying to figure out feasibility of new approach
>>> proposed
>>>>> on
>>>>>>> our
>>>>>>>> latest meeting - to have a single object which encapsulates
>multiple
>>>>>>> DPLLs.
>>>>>>>>
>>>>>>>> Please consider following example:
>>>>>>>>
>>>>>>>> Shared common inputs:
>>>>>>>> i0 - GPS  / external
>>>>>>>> i1 - SMA1 / external
>>>>>>>> i2 - SMA2 / external
>>>>>>>> i3 - MUX0 / clk recovered from PHY0.X driven by MAC0
>>>>>>>> i4 - MUX1 / clk recovered from PHY1.X driven by MAC1
>>>>>>>>
>>>>>>>> +---------------------------------------------------------+
>>>>>>>> | Channel A / FW0             +---+                       |
>>>>>>>> |                         i0--|   |                       |
>>>>>>>> |         +---+               |   |                       |
>>>>>>>> | PHY0.0--|   |           i1--| D |                       |
>>>>>>>> |         |   |               | P |                       |
>>>>>>>> | PHY0.1--| M |           i2--| L |   +---+   +--------+  |
>>>>>>>> |         | U |               | L |---|   |---| PHY0.0 |--|
>>>>>>>> | PHY0.2--| X |-+---------i3--| 0 |   |   |   +--------+  |
>>>>>>>> |         | 0 | |+------+     |   |---| M |---| PHY0.1 |--|
>>>>>>>> | ...   --|   | || MUX1 |-i4--|   |   | A |   +--------+  |
>>>>>>>> |         |   | |+------+     +---+   | C |---| PHY0.2 |--|
>>>>>>>> | PHY0.7--|   | |         i0--|   |   | 0 |   +--------+  |
>>>>>>>> |         +---+ |             |   |---|   |---| ...    |--|
>>>>>>>> |               |         i1--| D |   |   |   +--------+  |
>>>>>>>> |               |             | P |---|   |---| PHY0.7 |--|
>>>>>>>> |               |         i2--| L |   +---+   +--------+  |
>>>>>>>> |               |             | L |                       |
>>>>>>>> |               \---------i3--| 1 |                       |
>>>>>>>> |                +------+     |   |                       |
>>>>>>>> |                | MUX1 |-i4--|   |                       |
>>>>>>>> |                +------+     +---+                       |
>>>>>>>> +---------------------------------------------------------+
>>>>>>>> | Channel B / FW1             +---+                       |
>>>>>>>> |                         i0--|   |                       |
>>>>>>>> |                             |   |                       |
>>>>>>>> |                         i1--| D |                       |
>>>>>>>> |         +---+               | P |                       |
>>>>>>>> | PHY1.0--|   |           i2--| L |   +---+   +--------+  |
>>>>>>>> |         |   |  +------+     | L |---|   |---| PHY1.0 |--|
>>>>>>>> | PHY1.1--| M |  | MUX0 |-i3--| 0 |   |   |   +--------+  |
>>>>>>>> |         | U |  +------+     |   |---| M |---| PHY1.1 |--|
>>>>>>>> | PHY1.2--| X |-+---------i4--|   |   | A |   +--------+  |
>>>>>>>> |         | 1 | |             +---+   | C |---| PHY1.2 |--|
>>>>>>>> | ...   --|   | |         i0--|   |   | 1 |   +--------+  |
>>>>>>>> |         |   | |             |   |---|   |---| ...    |--|
>>>>>>>> | PHY1.7--|   | |         i1--| D |   |   |   +--------+  |
>>>>>>>> |         +---+ |             | P |---|   |---| PHY1.7 |--|
>>>>>>>> |               |         i2--| L |   +---+   +--------+  |
>>>>>>>> |               |+------+     | L |                       |
>>>>>>>> |               || MUX0 |-i3--| 1 |                       |
>>>>>>>> |               |+------+     |   |                       |
>>>>>>>> |               \---------i4--|   |                       |
>>>>>>>> |                             +---+                       |
>>>>>>>> +---------------------------------------------------------+
>>>>>>>
>>>>>>> What is "a channel" here? Are these 2 channels part of the same
>>> physival
>>>>>>> chip? Could you add the synchronizer chip/device entities to your
>>>>> drawing?
>>>>>>>
>>>>>>
>>>>>> No.
>>>>>> A "Synchronization Channel" on a switch would allow to separate
>groups
>>>>>> of physical ports. Each channel/group has own "Synchronizer Chip",
>>> which
>>>>> is
>>>>>> used to drive PHY clocks of that group.
>>>>>>
>>>>>> "Synchronizer chip" would be the 2 DPLLs on old draw, something like
>>>>> this:
>>>>>> +--------------------------------------------------------------+
>>>>>> | 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--|   |                                                |
>>>>>> |         +---+                                                |
>>>>>> +--------------------------------------------------------------+
>>>>>> Also, please keep in mind that is an example, there could be easily 4
>>>>>> (or more) channels wired similarly.
>>>>>>
>>>>>
>>>>>
>>>>> Hi,
>>>>>
>>>>> This model tries to put too much into the synchronizer subsystem. The
>>>>> synchronizer device should only model inputs, DPLLs and outputs.
>>>>>
>>>>> The PHY lane to Synchronizer input muxing should be done in the
>>>>> PHY/netdev subsystem. That's why I wanted to start with the full model
>>>>> to specifically address this topic.
>>>>>
>>>>> The netdev should have an assigned list of Synchronizer inputs that it
>>>>> can recover its SyncE clocks into. It can be done by having a
>connection
>>>>> between the synchronizer input object(s) and the netdev, just like the
>>>>> netdev is connected to PHC clocks in the PHC subsystem. This is the
>>>>> model I initially presented about a year ago for solving this specific
>>>>> issue.
>>>>>
>>>>> Analogically, the netdev will be connected to a given output, however
>>>>> changing anything in the physical clock configuration sounds
>dangerous.
>>>>>
>>>>> Does that sound reasonable?
>>>>>
>>>>> Regards
>>>>> Maciek
>>>>
>>>> It sounds reasonable to some point.
>>>> You have mentioned list of Synchronizer inputs. If there is a list of
>>> inputs
>>>> it means it was created somewhere. I assume dpll subsystem? If so you
>>> would
>>>> like to export that list out of dpll subsystem, thus other entities
>would
>>> need
>>>> to find such list, then find particular source and somehow register
>with
>>> it.
>>>> All of this was proposed as part of netdev, I don't see any benefit in
>>> having
>>>> this parts separated from dpll, as only dpll would use it, right?
>>>> The same behavior is now provided by the MUX type pin, enclosed within
>>> dpll
>>>> subsystem.
>>>>
>>>> BR,
>>>> Arkadiusz
>>>
>>> The synchronizer object should expose the list of inputs that represent
>>> possible sources of a given chip. The list will be the same for all
>>> DPLLs used by the same device, so it can be a single set of sources
>>> linked to multiple DPLLs inside the package. A netdev can then point to
>>> a given input of a synchronizer that it's connected to.
>>> The phy lane->recovered clock (or directly a synchronizer input) muxing
>>> should stay in the netdev subsystem, or in the PHY driver.
>>>
>>> The reason, and benefit, of such split is when you create a board with a
>>> netdev X and a synchronizer Y that is not instantiated by the same
>>> driver. In this scenario you'd get the ice driver to instantiate
>>> connections and the DPLL vendor's driver for the synchronizer. In such
>>> case the netdev driver will simply send a netlink message to the
>>> input/source with a requested configuration, such as expected frequency,
>>> and everything from this point can be handled by a completely different
>>> driver creating clean and logical split.
>>>
>>> If we mix the phy lanes into the DPLL subsystem it'll get very
>>> challenging to add PHY lanes to the existing synchronizer exposed by a
>>> different driver.
>>
>> This is possible right now:
>> 1. obtain a dpll object:
>> struct dpll_device *dpll_device_get_by_clock_id(u64 clock_id,
>> 						enum dpll_type type, u8 idx);
>> 2. register new pin with muxed type 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);
>>
>> To find dpll driver must know clock_id, type of dpll and its index given
>> when dpll was registered.
>> To register a pin, parent_pin_description of MUX type pin given on
>registering
>> it with dpll device.
>
>That would mean you need to repeat this process for all the DPLLs that
>are co-packaged in a single synchronizer. Some chips have up to 8 DPLLs,
>so you'd need to register number of phy lanes x number of DPLL times,
>say 8x8 = 64 times - that's simply too messy in the long term.
>

No, the pins are shared also MUX-type ones, adding to one adds to all.

>>> Exporting and link between the synchronizer and the netdev is still a
>>> must no matter which way we go. And IMO it's best to link netdev to
>>> synchronizer sources, as that's the most natural way.
>>>
>>
>> The link is now just information for userspace Linux network interface
>index
>> in DPLLA_PIN_NETIFINDEX attribute.
>
>That's not the right way. We need to know:
>- which DPLL pins/sources are driven by which netdev (to be able to
>  identify a source of frequency that is currently driving a given DPLL)

This is possible as explained above.

>- which DPLL generates a frequency for a given netdev (to know which
>  DPLL to check for a specific netdev)
>

I agree here, we need something like it.

BR,
Arkadiusz.

>So we need to have 2 connections.

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-11 16:14                                   ` Jiri Pirko
@ 2023-01-12 12:15                                     ` Kubalewski, Arkadiusz
  2023-01-12 14:43                                       ` Jiri Pirko
  0 siblings, 1 reply; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-12 12:15 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: Jakub Kicinski, Maciek Machnikowski, 'Vadim Fedorenko',
	'Jonathan Lemon', 'Paolo Abeni',
	netdev, linux-arm-kernel, linux-clk

>From: Jiri Pirko <jiri@resnulli.us>
>Sent: Wednesday, January 11, 2023 5:15 PM
>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>
>Wed, Jan 11, 2023 at 04:30:44PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>From: Jiri Pirko <jiri@resnulli.us>
>>>Sent: Wednesday, January 11, 2023 4:05 PM
>>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>>
>>>Wed, Jan 11, 2023 at 03:16:59PM CET, arkadiusz.kubalewski@intel.com
>wrote:
>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>Sent: Wednesday, January 11, 2023 9:20 AM
>>>>>
>>>>>Tue, Jan 10, 2023 at 09:05:49PM CET, kuba@kernel.org wrote:
>>>>>>On Mon, 9 Jan 2023 14:43:01 +0000 Kubalewski, Arkadiusz wrote:
>>>>>>> This is a simplified network switch board example.
>>>>>>> It has 2 synchronization channels, where each channel:
>>>>>>> - provides clk to 8 PHYs driven by separated MAC chips,
>>>>>>> - controls 2 DPLLs.
>>>>>>>
>>>>>>> Basically only given FW has control over its PHYs, so also a control
>>>>>over it's
>>>>>>> MUX inputs.
>>>>>>> All external sources are shared between the channels.
>>>>>>>
>>>>>>> This is why we believe it is not best idea to enclose multiple DPLLs
>>>>>with one
>>>>>>> object:
>>>>>>> - sources are shared even if DPLLs are not a single synchronizer
>chip,
>>>>>>> - control over specific MUX type input shall be controllable from
>>>>>different
>>>>>>> driver/firmware instances.
>>>>>>>
>>>>>>> As we know the proposal of having multiple DPLLs in one object was a
>>>try
>>>>>to
>>>>>>> simplify currently implemented shared pins. We fully support idea of
>>>>>having
>>>>>>> interfaces as simple as possible, but at the same time they shall be
>>>>>flexible
>>>>>>> enough to serve many use cases.
>>>>>>
>>>>>>I must be missing context from other discussions but what is this
>>>>>>proposal trying to solve? Well implemented shared pins is all we need.
>>>>>
>>>>>There is an entity containing the pins. The synchronizer chip. One
>>>>>synchronizer chip contains 1-n DPLLs. The source pins are connected
>>>>>to each DPLL (usually). What we missed in the original model was the
>>>>>synchronizer entity. If we have it, we don't need any notion of somehow
>>>>>floating pins as independent entities being attached to one or many
>>>>>DPLL refcounted, etc. The synchronizer device holds them in
>>>>>straightforward way.
>>>>>
>>>>>Example of a synchronizer chip:
>>>>>https://www.renesas.com/us/en/products/clocks-timing/jitter-
>attenuators-
>>>>>frequency-translation/8a34044-multichannel-dpll-dco-four-eight-
>>>>>channels#overview
>>>>
>>>>Not really, as explained above, multiple separated synchronizer chips
>can
>>>be
>>>>connected to the same external sources.
>>>>This is why I wrote this email, to better explain need for references
>>>between
>>>>DPLLs and shared pins.
>>>>Synchronizer chip object with multiple DPLLs would have sense if the
>pins
>>>would
>>>>only belong to that single chip, but this is not true.
>>>
>>>I don't understand how it is physically possible that 2 pins belong to 2
>>>chips. Could you draw this to me?
>>>
>>
>>Well, sure, I was hoping this is clear, without extra connections on the
>draw:
>
>Okay, now I understand. It is not a shared pin but shared source for 2
>pins.
>

Yes, exactly.

>
>>+----------+
>>|i0 - GPS  |--------------\
>>+----------+              |
>>+----------+              |
>>|i1 - SMA1 |------------\ |
>>+----------+            | |
>>+----------+            | |
>>|i2 - SMA2 |----------\ | |
>>+----------+          | | |
>>                      | | |
>>+---------------------|-|-|-------------------------------------------+
>>| Channel A / FW0     | | |     +-------------+   +---+   +--------+  |
>>|                     | | |-i0--|Synchronizer0|---|   |---| PHY0.0 |--|
>
>One pin here               ^^^
>
>>|         +---+       | | |     |             |   |   |   +--------+  |
>>| 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 |--|
>
>And second pin here        ^^^
>
>There are 2 separate pins. Sure, they need to have the same config as
>they are connected to the same external entity (GPS, SMA1, SMA2).
>
>Perhaps we need to have a board description using dts to draw this
>picture so the drivers can use this schema in order to properly
>configure this?
>
>My point is, you are trying to hardcode the board geometry in the
>driver. Is that correct?
>

Well, we are trying to have userspace-friendly interface :)
As we discussed yesterday dts is more of embedded world thing and we don't
want to go that far, the driver knows the hardware it is using, thus it
shall be enough if it has all the information needed for initialization.
At least that is what I understood.

BR,
Arkadiusz

>
>>|         +---+  | |  | |       |             |   |   |   +--------+  |
>>| 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--|   |                                                       |
>>|         +---+                                                       |
>>+---------------------------------------------------------------------+
>>
>>>
>>>>As the pins are shared between multiple DPLLs (both inside 1 integrated
>>>circuit
>>>>and between multiple integrated circuits), all of them shall have
>current
>>>state
>>>>of the source or output.
>>>>Pins still need to be shared same as they would be inside of one
>>>synchronizer
>>>>chip.
>>>
>>>Do I understand correctly that you connect one synchronizer output to
>>>the input of the second synchronizer chip?
>>
>>No, I don't recall such use case. At least nothing that needs to exposed
>>in the DPLL subsystem itself.
>>
>>BR,
>>Arkadiusz
>>
>>>
>>>>
>>>>BR,
>>>>Arkadiusz

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-11-29 21:37 [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Vadim Fedorenko
                   ` (4 preceding siblings ...)
  2022-11-30 12:32 ` [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Jiri Pirko
@ 2023-01-12 12:23 ` Kubalewski, Arkadiusz
  2023-01-12 14:50   ` Jiri Pirko
  2023-01-12 19:09   ` Jakub Kicinski
  5 siblings, 2 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-12 12:23 UTC (permalink / raw)
  To: Vadim Fedorenko, Jakub Kicinski, Jiri Pirko, Jonathan Lemon, Paolo Abeni
  Cc: netdev, linux-arm-kernel, linux-clk

>From: Vadim Fedorenko <vfedorenko@novek.ru>
>Sent: Tuesday, November 29, 2022 10:37 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.
>
>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 (1):
>  dpll: add dpll_attr/dpll_pin_attr helper classes
>
>Vadim Fedorenko (3):
>  dpll: Add DPLL framework base functions
>  dpll: documentation on DPLL subsystem interface
>  ptp_ocp: implement DPLL ops
>
> Documentation/networking/dpll.rst  | 271 ++++++++
> Documentation/networking/index.rst |   1 +
> MAINTAINERS                        |   8 +
> drivers/Kconfig                    |   2 +
> drivers/Makefile                   |   1 +
> drivers/dpll/Kconfig               |   7 +
> drivers/dpll/Makefile              |  11 +
> drivers/dpll/dpll_attr.c           | 278 +++++++++
> drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
> drivers/dpll/dpll_core.h           | 176 ++++++
> drivers/dpll/dpll_netlink.c        | 963 +++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h        |  24 +
> drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
> drivers/ptp/Kconfig                |   1 +
> drivers/ptp/ptp_ocp.c              | 123 ++--
> include/linux/dpll.h               | 261 ++++++++
> include/linux/dpll_attr.h          | 433 +++++++++++++
> include/uapi/linux/dpll.h          | 263 ++++++++
> 18 files changed, 4002 insertions(+), 37 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_attr.c  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/dpll/dpll_pin_attr.c  create mode 100644
>include/linux/dpll.h  create mode 100644 include/linux/dpll_attr.h  create
>mode 100644 include/uapi/linux/dpll.h
>
>--
>2.27.0

New thread with regard of our yesterday's call.

Is it possible to initialize a multiple output MUX?
Yes it is. Let's consider 4 input/2 output MUX and DPLL it connects with:
            +---+   
          --|   |   
  +---+     |   |   
--|   |   --| D |--
  |   |     | P |   
--| M |-----| L |--
  | U |     | L |   
--| X |-----|   |--
  |   |     |   |   
--|   |   --|   |   
  +---+     +---+  
 
Basically dpll pins are initialized and assigned ids, like:
5 inputs (0-4), 3 outputs (5-7).
   +---+   
0--|   |   
   |   |   
1--| D |--5
   | P |   
2--| L |--6
   | L |   
3--|   |--7
   |   |   
4--|   |   
   +---+

Then we would create and register muxed pins with existing dpll pins.
Each muxed pin is allocated and registered with each parent it can provide
signal with, like below (number in bracket is parent idx):
                           +---+   
                        0--|   |   
                +---+      |   |   
 8(2) /  9(3)---|   |   1--| D |--5
                |   |      | P |   
10(2) / 11(3)---| M |---2--| L |--6
                | U |      | L |   
12(2) / 13(3)---| X |---3--|   |--7
                |   |      |   |   
14(2) / 15(3)---|   |   4--|   |   
                +---+      +---+

Controlling the mux input/output:
In this case selecting pin #8 would provide its signal into DPLLs input#2 and
selecting #9 would provide its signal into DPLLs input#3.

BR,
Arkadiusz

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

* RE: [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
  2022-12-19  9:13   ` Paolo Abeni
@ 2023-01-12 13:45     ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 87+ messages in thread
From: Kubalewski, Arkadiusz @ 2023-01-12 13:45 UTC (permalink / raw)
  To: Paolo Abeni, Vadim Fedorenko, Jakub Kicinski, Jiri Pirko, Jonathan Lemon
  Cc: netdev, Vadim Fedorenko, linux-arm-kernel, linux-clk

>From: Paolo Abeni <pabeni@redhat.com>
>Sent: Monday, December 19, 2022 10:13 AM
>
>Hello,
>
>I have a just a few minor notes WRT the documentation - which was a
>very useful entry point for me to help understanding the subsystem.

Hi Paolo, many thanks for your feedback!

>
>On Wed, 2022-11-30 at 00:37 +0300, Vadim Fedorenko wrote:
>> From: Vadim Fedorenko <vadfed@fb.com>
>>
>> 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@fb.com>
>> ---
>>  Documentation/networking/dpll.rst  | 271 +++++++++++++++++++++++++++++
>>  Documentation/networking/index.rst |   1 +
>>  2 files changed, 272 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..58401e2b70a7
>> --- /dev/null
>> +++ b/Documentation/networking/dpll.rst
>> @@ -0,0 +1,271 @@
>> +.. 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_SGINAL_TYPE, DPLL_PIN_TYPE,
>
>Likely typo above: DPLL_PIN_SIGNAL_TYPE

True, shall be fixed in next version.

>
>> +DPLL_PIN_STATE), 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.
>
>Side note: in the long run we could end-up with a virtual/dummy dpll
>driver for self-tests and/or reference's implementation sake.
>

True, seems a good idea.

>> +
>> +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
>> +    ``NETIFINDEX``              attr dpll owner Linux netdevice index
>
>should we include also the cookie (or wuhatever will be used for
>persistent device identification) into the readable attributes list?
>

In next version cookie is replaced with clock_id and will be also available
for the userspace.

>> +  ``DEVICE_SET``                userspace to set dpll device
>> +                                configuration
>> +    ``ID``                      attr internal dpll device index
>> +    ``MODE``                    attr selection mode to configure
>> +    ``PIN_IDX``                 attr index of source pin to select as
>> +                                active source
>
>It looks like the descrition for the above attribute ('PIN_IDX') and
>'SOURCE_PIN_IDX' has been swapped.

Good catch, for ``DEVICE_SET`` command, proper attribute is 'SOURCE_PIN_IDX',
will fix that.

>
>> +  ``PIN_SET``                   userspace to set pins configuration
>> +    ``ID``                      attr internal dpll device index
>> +    ``PIN_IDX``                 attr index of a pin to configure
>> +    ``PIN_TYPE``                attr type configuration value for
>> +                                selected pin
>> +    ``PIN_SIGNAL_TYPE``         attr signal type configuration value
>> +                                for selected pin
>> +    ``PIN_CUSTOM_FREQ``         attr signal custom frequency to be set
>> +    ``PIN_STATE``               attr pin state to be set
>> +    ``PIN_PRIO``                attr pin priority to be set
>> +
>> +Netlink dump requests
>> +=====================
>> +The ``DEVICE_GET`` command is capable of dump type netlink requests.
>> +In such case the userspace shall provide ``DUMP_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.
>
>Should we explicitly document even the required permissions?

Sure, going to add a word about required netlink permission.

>
>> +
>> +Possible response message attributes for netlink requests depending on
>> +the value of ``DPLLA_DUMP_FILTER`` attribute:
>> +
>> +  =============================== ====================================
>> +  ``DPLL_DUMP_FILTER_PINS``       value of ``DUMP_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_TYPE_SUPPORTED``        attr value of supported 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_STATE``                 attr value of pin state
>> +    ``PIN_STATE_SUPPORTED``       attr value of supported pin state
>> +    ``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_DUMP_FILTER_STATUS``     value of ``DUMP_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
>> +    ``NETIFINDEX``                attr dpll owner Linux netdevice index
>> +
>> +
>> +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_STATE`` and ``PIN_STATE_SUPPORTED`` attributes:
>> +
>> +============================= ============================
>> +  ``PIN_STATE_CONNECTED``     Pin connected to a dpll
>> +  ``PIN_STATE_DISCONNECTED``  Pin disconnected from dpll
>> +  ``PIN_STATE_SOURCE``        Source pin
>> +  ``PIN_STATE_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 ise auto selected according to
>
>typo above 'ise' -> 'is'
>

Sure, will fix.

Thanks again!

BR,
Arkadiusz

>
>Cheers,
>
>Paolo


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

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

Thu, Jan 12, 2023 at 01:15:30PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Jiri Pirko <jiri@resnulli.us>
>>Sent: Wednesday, January 11, 2023 5:15 PM
>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>
>>Wed, Jan 11, 2023 at 04:30:44PM CET, arkadiusz.kubalewski@intel.com wrote:
>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>Sent: Wednesday, January 11, 2023 4:05 PM
>>>>To: Kubalewski, Arkadiusz <arkadiusz.kubalewski@intel.com>
>>>>
>>>>Wed, Jan 11, 2023 at 03:16:59PM CET, arkadiusz.kubalewski@intel.com
>>wrote:
>>>>>>From: Jiri Pirko <jiri@resnulli.us>
>>>>>>Sent: Wednesday, January 11, 2023 9:20 AM
>>>>>>
>>>>>>Tue, Jan 10, 2023 at 09:05:49PM CET, kuba@kernel.org wrote:
>>>>>>>On Mon, 9 Jan 2023 14:43:01 +0000 Kubalewski, Arkadiusz wrote:
>>>>>>>> This is a simplified network switch board example.
>>>>>>>> It has 2 synchronization channels, where each channel:
>>>>>>>> - provides clk to 8 PHYs driven by separated MAC chips,
>>>>>>>> - controls 2 DPLLs.
>>>>>>>>
>>>>>>>> Basically only given FW has control over its PHYs, so also a control
>>>>>>over it's
>>>>>>>> MUX inputs.
>>>>>>>> All external sources are shared between the channels.
>>>>>>>>
>>>>>>>> This is why we believe it is not best idea to enclose multiple DPLLs
>>>>>>with one
>>>>>>>> object:
>>>>>>>> - sources are shared even if DPLLs are not a single synchronizer
>>chip,
>>>>>>>> - control over specific MUX type input shall be controllable from
>>>>>>different
>>>>>>>> driver/firmware instances.
>>>>>>>>
>>>>>>>> As we know the proposal of having multiple DPLLs in one object was a
>>>>try
>>>>>>to
>>>>>>>> simplify currently implemented shared pins. We fully support idea of
>>>>>>having
>>>>>>>> interfaces as simple as possible, but at the same time they shall be
>>>>>>flexible
>>>>>>>> enough to serve many use cases.
>>>>>>>
>>>>>>>I must be missing context from other discussions but what is this
>>>>>>>proposal trying to solve? Well implemented shared pins is all we need.
>>>>>>
>>>>>>There is an entity containing the pins. The synchronizer chip. One
>>>>>>synchronizer chip contains 1-n DPLLs. The source pins are connected
>>>>>>to each DPLL (usually). What we missed in the original model was the
>>>>>>synchronizer entity. If we have it, we don't need any notion of somehow
>>>>>>floating pins as independent entities being attached to one or many
>>>>>>DPLL refcounted, etc. The synchronizer device holds them in
>>>>>>straightforward way.
>>>>>>
>>>>>>Example of a synchronizer chip:
>>>>>>https://www.renesas.com/us/en/products/clocks-timing/jitter-
>>attenuators-
>>>>>>frequency-translation/8a34044-multichannel-dpll-dco-four-eight-
>>>>>>channels#overview
>>>>>
>>>>>Not really, as explained above, multiple separated synchronizer chips
>>can
>>>>be
>>>>>connected to the same external sources.
>>>>>This is why I wrote this email, to better explain need for references
>>>>between
>>>>>DPLLs and shared pins.
>>>>>Synchronizer chip object with multiple DPLLs would have sense if the
>>pins
>>>>would
>>>>>only belong to that single chip, but this is not true.
>>>>
>>>>I don't understand how it is physically possible that 2 pins belong to 2
>>>>chips. Could you draw this to me?
>>>>
>>>
>>>Well, sure, I was hoping this is clear, without extra connections on the
>>draw:
>>
>>Okay, now I understand. It is not a shared pin but shared source for 2
>>pins.
>>
>
>Yes, exactly.
>
>>
>>>+----------+
>>>|i0 - GPS  |--------------\
>>>+----------+              |
>>>+----------+              |
>>>|i1 - SMA1 |------------\ |
>>>+----------+            | |
>>>+----------+            | |
>>>|i2 - SMA2 |----------\ | |
>>>+----------+          | | |
>>>                      | | |
>>>+---------------------|-|-|-------------------------------------------+
>>>| Channel A / FW0     | | |     +-------------+   +---+   +--------+  |
>>>|                     | | |-i0--|Synchronizer0|---|   |---| PHY0.0 |--|
>>
>>One pin here               ^^^
>>
>>>|         +---+       | | |     |             |   |   |   +--------+  |
>>>| 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 |--|
>>
>>And second pin here        ^^^
>>
>>There are 2 separate pins. Sure, they need to have the same config as
>>they are connected to the same external entity (GPS, SMA1, SMA2).
>>
>>Perhaps we need to have a board description using dts to draw this
>>picture so the drivers can use this schema in order to properly
>>configure this?
>>
>>My point is, you are trying to hardcode the board geometry in the
>>driver. Is that correct?
>>
>
>Well, we are trying to have userspace-friendly interface :)
>As we discussed yesterday dts is more of embedded world thing and we don't
>want to go that far, the driver knows the hardware it is using, thus it
>shall be enough if it has all the information needed for initialization.
>At least that is what I understood.

Yes, I think yesterday called cleared things up. Thanks!


>
>BR,
>Arkadiusz
>
>>
>>>|         +---+  | |  | |       |             |   |   |   +--------+  |
>>>| 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--|   |                                                       |
>>>|         +---+                                                       |
>>>+---------------------------------------------------------------------+
>>>
>>>>
>>>>>As the pins are shared between multiple DPLLs (both inside 1 integrated
>>>>circuit
>>>>>and between multiple integrated circuits), all of them shall have
>>current
>>>>state
>>>>>of the source or output.
>>>>>Pins still need to be shared same as they would be inside of one
>>>>synchronizer
>>>>>chip.
>>>>
>>>>Do I understand correctly that you connect one synchronizer output to
>>>>the input of the second synchronizer chip?
>>>
>>>No, I don't recall such use case. At least nothing that needs to exposed
>>>in the DPLL subsystem itself.
>>>
>>>BR,
>>>Arkadiusz
>>>
>>>>
>>>>>
>>>>>BR,
>>>>>Arkadiusz

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

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

Thu, Jan 12, 2023 at 01:23:29PM CET, arkadiusz.kubalewski@intel.com wrote:
>>From: Vadim Fedorenko <vfedorenko@novek.ru>
>>Sent: Tuesday, November 29, 2022 10:37 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.
>>
>>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 (1):
>>  dpll: add dpll_attr/dpll_pin_attr helper classes
>>
>>Vadim Fedorenko (3):
>>  dpll: Add DPLL framework base functions
>>  dpll: documentation on DPLL subsystem interface
>>  ptp_ocp: implement DPLL ops
>>
>> Documentation/networking/dpll.rst  | 271 ++++++++
>> Documentation/networking/index.rst |   1 +
>> MAINTAINERS                        |   8 +
>> drivers/Kconfig                    |   2 +
>> drivers/Makefile                   |   1 +
>> drivers/dpll/Kconfig               |   7 +
>> drivers/dpll/Makefile              |  11 +
>> drivers/dpll/dpll_attr.c           | 278 +++++++++
>> drivers/dpll/dpll_core.c           | 760 +++++++++++++++++++++++
>> drivers/dpll/dpll_core.h           | 176 ++++++
>> drivers/dpll/dpll_netlink.c        | 963 +++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h        |  24 +
>> drivers/dpll/dpll_pin_attr.c       | 456 ++++++++++++++
>> drivers/ptp/Kconfig                |   1 +
>> drivers/ptp/ptp_ocp.c              | 123 ++--
>> include/linux/dpll.h               | 261 ++++++++
>> include/linux/dpll_attr.h          | 433 +++++++++++++
>> include/uapi/linux/dpll.h          | 263 ++++++++
>> 18 files changed, 4002 insertions(+), 37 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_attr.c  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/dpll/dpll_pin_attr.c  create mode 100644
>>include/linux/dpll.h  create mode 100644 include/linux/dpll_attr.h  create
>>mode 100644 include/uapi/linux/dpll.h
>>
>>--
>>2.27.0
>
>New thread with regard of our yesterday's call.
>
>Is it possible to initialize a multiple output MUX?
>Yes it is. Let's consider 4 input/2 output MUX and DPLL it connects with:
>            +---+   
>          --|   |   
>  +---+     |   |   
>--|   |   --| D |--
>  |   |     | P |   
>--| M |-----| L |--
>  | U |     | L |   
>--| X |-----|   |--
>  |   |     |   |   
>--|   |   --|   |   
>  +---+     +---+  
> 
>Basically dpll pins are initialized and assigned ids, like:
>5 inputs (0-4), 3 outputs (5-7).
>   +---+   
>0--|   |   
>   |   |   
>1--| D |--5
>   | P |   
>2--| L |--6
>   | L |   
>3--|   |--7
>   |   |   
>4--|   |   
>   +---+
>
>Then we would create and register muxed pins with existing dpll pins.
>Each muxed pin is allocated and registered with each parent it can provide
>signal with, like below (number in bracket is parent idx):
>                           +---+   
>                        0--|   |   
>                +---+      |   |   
> 8(2) /  9(3)---|   |   1--| D |--5
>                |   |      | P |   
>10(2) / 11(3)---| M |---2--| L |--6
>                | U |      | L |   
>12(2) / 13(3)---| X |---3--|   |--7
>                |   |      |   |   
>14(2) / 15(3)---|   |   4--|   |   
>                +---+      +---+
>
>Controlling the mux input/output:
>In this case selecting pin #8 would provide its signal into DPLLs input#2 and
>selecting #9 would provide its signal into DPLLs input#3.

Duplication of the mux pin (for example 8,9) seems a bit silly. What if
the mux has 8 outputs? You would have to have 8 pin indexes for each mux
input.

One thing is not clear to me. The mux outputs are fixed or selectable?
I mean, can the outputs be enabled/disabled wired to a specific mux
input?


>
>BR,
>Arkadiusz

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

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

On Thu, 12 Jan 2023 12:23:29 +0000 Kubalewski, Arkadiusz wrote:
> Then we would create and register muxed pins with existing dpll pins.
> Each muxed pin is allocated and registered with each parent it can provide
> signal with, like below (number in bracket is parent idx):
>                            +---+   
>                         0--|   |   
>                 +---+      |   |   
>  8(2) /  9(3)---|   |   1--| D |--5
>                 |   |      | P |   
> 10(2) / 11(3)---| M |---2--| L |--6
>                 | U |      | L |   
> 12(2) / 13(3)---| X |---3--|   |--7
>                 |   |      |   |   
> 14(2) / 15(3)---|   |   4--|   |   
>                 +---+      +---+
> 
> Controlling the mux input/output:
> In this case selecting pin #8 would provide its signal into DPLLs input#2 and
> selecting #9 would provide its signal into DPLLs input#3.

I agree with Jiri, the duplication seems unnecessary. My thinking would
be to handle this as follows:

                             +---+   
                          0--|   |   
                +---+        |   |   
           10---|   |     1--| D |--5
                |   |        | P |   
           11---| M |-8---2--| L |--6
                | U |        | L |   
           12---| X |-9---3--|   |--7
                |   |        |   |   
           13---|   |     4--|   |   
                +---+        +---+

Give the user the ability to both select the inputs to DPLL from 
0-4 and from 10-13. If 10-13 are selected the core should give mapping
things automatically a try (but we don't need to support auto-mapping 
for muxes with more than one output from the start).
There should also be an API for manually configuring muxes. 

Eg.

User requests DPLL inputs: 0, 1, 10, 11
  Core automatically maps 10 -> 8, 11 -> 9

User requests DPLL inputs: 0, 1, 10, 11, 12
  Core responds with an error

User requests DPLL inputs: 0, 1, 2, 3
  Core doesn't touch the mux
User requests mux to direct 10 -> 8
User requests mux to direct 11 -> 9
  Now the config is equivalent to case #1

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

end of thread, other threads:[~2023-01-12 19:22 UTC | newest]

Thread overview: 87+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-29 21:37 [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Vadim Fedorenko
2022-11-29 21:37 ` [RFC PATCH v4 1/4] dpll: add dpll_attr/dpll_pin_attr helper classes Vadim Fedorenko
2022-11-29 21:37 ` [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions Vadim Fedorenko
2022-11-30 15:21   ` Jiri Pirko
2022-11-30 16:23     ` Jiri Pirko
2022-12-23 16:45     ` Kubalewski, Arkadiusz
2023-01-02 12:28       ` Jiri Pirko
2022-11-30 16:37   ` Jiri Pirko
2022-12-02 11:27     ` Kubalewski, Arkadiusz
2022-12-02 12:39       ` Jiri Pirko
2022-12-02 14:54         ` Kubalewski, Arkadiusz
2022-12-02 16:15           ` Jiri Pirko
     [not found]             ` <20221202212206.3619bd5f@kernel.org>
2022-12-05 10:32               ` Jiri Pirko
2022-12-06  0:19                 ` Jakub Kicinski
2022-12-06  8:50                   ` Jiri Pirko
2022-12-06 17:27                     ` Jakub Kicinski
2022-12-07 13:10                       ` Jiri Pirko
2022-12-07 16:59                         ` Jakub Kicinski
2022-12-08  8:14                           ` Jiri Pirko
2022-12-08 16:19                             ` Jakub Kicinski
2022-12-08 16:33                               ` Jiri Pirko
2022-12-08 17:05                                 ` Jakub Kicinski
2022-12-09  9:29                                   ` Jiri Pirko
2022-12-09 16:19                                     ` Jakub Kicinski
2022-12-12 13:36                                       ` Jiri Pirko
2022-12-13 18:08                                         ` Kubalewski, Arkadiusz
2022-12-14  7:32                                           ` Jiri Pirko
2022-11-29 21:37 ` [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface Vadim Fedorenko
2022-12-19  9:13   ` Paolo Abeni
2023-01-12 13:45     ` Kubalewski, Arkadiusz
2022-11-29 21:37 ` [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops Vadim Fedorenko
2022-11-30 12:41   ` Jiri Pirko
2022-12-02 11:27     ` Kubalewski, Arkadiusz
2022-12-02 12:48       ` Jiri Pirko
2022-12-02 14:39         ` Kubalewski, Arkadiusz
2022-12-02 16:20           ` Jiri Pirko
2022-12-08  0:35             ` Kubalewski, Arkadiusz
2022-12-08  8:19               ` Jiri Pirko
2022-12-07  2:33           ` Jakub Kicinski
2022-12-07 13:19             ` Jiri Pirko
     [not found]               ` <20221207090524.3f562eeb@kernel.org>
2022-12-08 11:22                 ` Jiri Pirko
2022-12-09  0:36                   ` Jakub Kicinski
2022-12-09  9:32                     ` Jiri Pirko
2022-11-30 12:32 ` [RFC PATCH v4 0/4] Create common DPLL/clock configuration API Jiri Pirko
2022-12-02 11:27   ` Kubalewski, Arkadiusz
2022-12-02 16:12     ` Jiri Pirko
2022-12-07  2:47       ` Jakub Kicinski
2022-12-07 14:09         ` netdev.dump
2022-12-07 23:21           ` Jakub Kicinski
2022-12-08 11:28             ` Jiri Pirko
2022-12-09  0:39               ` Jakub Kicinski
2022-12-09  0:56                 ` Kubalewski, Arkadiusz
2022-12-08 18:08             ` Maciek Machnikowski
2022-12-09 11:07               ` Jiri Pirko
2022-12-09 14:09                 ` Maciek Machnikowski
2022-12-09 16:31                   ` Jakub Kicinski
2022-12-09 17:11                     ` Maciek Machnikowski
2022-12-12 13:58                     ` Jiri Pirko
2023-01-09 14:43                       ` Kubalewski, Arkadiusz
2023-01-09 16:30                         ` Jiri Pirko
2023-01-10 10:54                           ` Kubalewski, Arkadiusz
2023-01-10 14:28                             ` Jiri Pirko
     [not found]                             ` <645a5bfd-0092-2f39-0ff2-3ffb27ccf8fe@machnikowski.net>
2023-01-11 14:17                               ` Kubalewski, Arkadiusz
2023-01-11 14:40                                 ` Maciek Machnikowski
2023-01-11 15:30                                   ` Kubalewski, Arkadiusz
2023-01-11 15:54                                     ` Maciek Machnikowski
2023-01-11 16:27                                       ` Kubalewski, Arkadiusz
2023-01-10 20:05                         ` Jakub Kicinski
2023-01-11  8:19                           ` Jiri Pirko
2023-01-11 14:16                             ` Kubalewski, Arkadiusz
2023-01-11 15:04                               ` Jiri Pirko
2023-01-11 15:30                                 ` Kubalewski, Arkadiusz
2023-01-11 16:14                                   ` Jiri Pirko
2023-01-12 12:15                                     ` Kubalewski, Arkadiusz
2023-01-12 14:43                                       ` Jiri Pirko
2022-12-09  0:46             ` Kubalewski, Arkadiusz
2022-12-07 14:51         ` Jiri Pirko
     [not found]           ` <20221207091946.3115742f@kernel.org>
2022-12-08 12:02             ` Jiri Pirko
2022-12-09  0:54               ` Jakub Kicinski
2022-12-08 18:23             ` Kubalewski, Arkadiusz
2022-12-08  0:27       ` Kubalewski, Arkadiusz
2022-12-08 11:58         ` Jiri Pirko
2022-12-08 23:05           ` Kubalewski, Arkadiusz
2022-12-09 10:01             ` Jiri Pirko
2023-01-12 12:23 ` Kubalewski, Arkadiusz
2023-01-12 14:50   ` Jiri Pirko
2023-01-12 19:09   ` Jakub Kicinski

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