All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-11-29 21:37 ` Vadim Fedorenko
  0 siblings, 0 replies; 172+ 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


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

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

* [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-11-29 21:37 ` Vadim Fedorenko
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

* [RFC PATCH v4 1/4] dpll: add dpll_attr/dpll_pin_attr helper classes
  2022-11-29 21:37 ` Vadim Fedorenko
@ 2022-11-29 21:37   ` Vadim Fedorenko
  -1 siblings, 0 replies; 172+ 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


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

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

* [RFC PATCH v4 1/4] dpll: add dpll_attr/dpll_pin_attr helper classes
@ 2022-11-29 21:37   ` Vadim Fedorenko
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

* [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-29 21:37 ` Vadim Fedorenko
@ 2022-11-29 21:37   ` Vadim Fedorenko
  -1 siblings, 0 replies; 172+ 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


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

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

* [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-11-29 21:37   ` Vadim Fedorenko
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

* [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
  2022-11-29 21:37 ` Vadim Fedorenko
@ 2022-11-29 21:37   ` Vadim Fedorenko
  -1 siblings, 0 replies; 172+ 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


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

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

* [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
@ 2022-11-29 21:37   ` Vadim Fedorenko
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

* [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-11-29 21:37 ` Vadim Fedorenko
@ 2022-11-29 21:37   ` Vadim Fedorenko
  -1 siblings, 0 replies; 172+ 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


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

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

* [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-11-29 21:37   ` Vadim Fedorenko
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-11-29 21:37   ` Vadim Fedorenko
  (?)
@ 2022-11-30  8:30   ` kernel test robot
  -1 siblings, 0 replies; 172+ messages in thread
From: kernel test robot @ 2022-11-30  8:30 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: oe-kbuild-all

[-- Attachment #1: Type: text/plain, Size: 4933 bytes --]

Hi Vadim,

[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on linus/master]
[also build test WARNING on v6.1-rc7 next-20221130]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/Create-common-DPLL-clock-configuration-API/20221130-094051
patch link:    https://lore.kernel.org/r/20221129213724.10119-5-vfedorenko%40novek.ru
patch subject: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
config: sparc-allyesconfig
compiler: sparc64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/b7080b6877127b717970130f1cec1c2bc35a82c8
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/Create-common-DPLL-clock-configuration-API/20221130-094051
        git checkout b7080b6877127b717970130f1cec1c2bc35a82c8
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc SHELL=/bin/bash drivers/dpll/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/dpll/dpll_core.c: In function 'dpll_pin_alloc':
>> drivers/dpll/dpll_core.c:266:9: warning: 'strncpy' specified bound 20 equals destination size [-Wstringop-truncation]
     266 |         strncpy(pin->description, description, PIN_DESC_LEN);
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
>> drivers/dpll/dpll_netlink.c:626:16: warning: no previous prototype for 'dpll_msg_read_mode' [-Wmissing-prototypes]
     626 | enum dpll_mode dpll_msg_read_mode(struct nlattr *a)
         |                ^~~~~~~~~~~~~~~~~~
>> drivers/dpll/dpll_netlink.c:631:5: warning: no previous prototype for 'dpll_msg_read_source_pin_id' [-Wmissing-prototypes]
     631 | u32 dpll_msg_read_source_pin_id(struct nlattr *a)
         |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/dpll/dpll_netlink.c:776:39: warning: 'struct genl_split_ops' declared inside parameter list will not be visible outside of this definition or declaration
     776 | static int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
         |                                       ^~~~~~~~~~~~~~
   drivers/dpll/dpll_netlink.c:842:27: error: initialization of 'int (*)(const struct genl_ops *, struct sk_buff *, struct genl_info *)' from incompatible pointer type 'int (*)(const struct genl_split_ops *, struct sk_buff *, struct genl_info *)' [-Werror=incompatible-pointer-types]
     842 |         .pre_doit       = dpll_pre_doit,
         |                           ^~~~~~~~~~~~~
   drivers/dpll/dpll_netlink.c:842:27: note: (near initialization for 'dpll_family.pre_doit')
>> drivers/dpll/dpll_netlink.c:960:13: warning: no previous prototype for 'dpll_netlink_fini' [-Wmissing-prototypes]
     960 | void __exit dpll_netlink_fini(void)
         |             ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/strncpy +266 drivers/dpll/dpll_core.c

316427bde82fc2 Vadim Fedorenko 2022-11-30  256  
316427bde82fc2 Vadim Fedorenko 2022-11-30  257  struct dpll_pin *dpll_pin_alloc(const char *description, size_t desc_len)
316427bde82fc2 Vadim Fedorenko 2022-11-30  258  {
316427bde82fc2 Vadim Fedorenko 2022-11-30  259  	struct dpll_pin *pin = kzalloc(sizeof(struct dpll_pin), GFP_KERNEL);
316427bde82fc2 Vadim Fedorenko 2022-11-30  260  
316427bde82fc2 Vadim Fedorenko 2022-11-30  261  	if (!pin)
316427bde82fc2 Vadim Fedorenko 2022-11-30  262  		return ERR_PTR(-ENOMEM);
316427bde82fc2 Vadim Fedorenko 2022-11-30  263  	if (desc_len > PIN_DESC_LEN)
316427bde82fc2 Vadim Fedorenko 2022-11-30  264  		return ERR_PTR(-EINVAL);
316427bde82fc2 Vadim Fedorenko 2022-11-30  265  
316427bde82fc2 Vadim Fedorenko 2022-11-30 @266  	strncpy(pin->description, description, PIN_DESC_LEN);
316427bde82fc2 Vadim Fedorenko 2022-11-30  267  	if (desc_len == PIN_DESC_LEN)
316427bde82fc2 Vadim Fedorenko 2022-11-30  268  		pin->description[PIN_DESC_LEN - 1] = '\0';
316427bde82fc2 Vadim Fedorenko 2022-11-30  269  	xa_init_flags(&pin->ref_dplls, XA_FLAGS_ALLOC);
316427bde82fc2 Vadim Fedorenko 2022-11-30  270  
316427bde82fc2 Vadim Fedorenko 2022-11-30  271  	return pin;
316427bde82fc2 Vadim Fedorenko 2022-11-30  272  }
316427bde82fc2 Vadim Fedorenko 2022-11-30  273  EXPORT_SYMBOL_GPL(dpll_pin_alloc);
316427bde82fc2 Vadim Fedorenko 2022-11-30  274  

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

[-- Attachment #2: config --]
[-- Type: text/plain, Size: 322382 bytes --]

#
# Automatically generated file; DO NOT EDIT.
# Linux/sparc 6.1.0-rc7 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="sparc64-linux-gcc (GCC) 12.1.0"
CONFIG_CC_IS_GCC=y
CONFIG_GCC_VERSION=120100
CONFIG_CLANG_VERSION=0
CONFIG_AS_IS_GNU=y
CONFIG_AS_VERSION=23800
CONFIG_LD_IS_BFD=y
CONFIG_LD_VERSION=23800
CONFIG_LLD_VERSION=0
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
CONFIG_CC_HAS_ASM_INLINE=y
CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y
CONFIG_PAHOLE_VERSION=123
CONFIG_CONSTRUCTORS=y
CONFIG_IRQ_WORK=y

#
# General setup
#
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_COMPILE_TEST=y
# CONFIG_WERROR is not set
CONFIG_LOCALVERSION=""
CONFIG_BUILD_SALT=""
CONFIG_DEFAULT_INIT=""
CONFIG_DEFAULT_HOSTNAME="(none)"
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
CONFIG_SYSVIPC_COMPAT=y
CONFIG_POSIX_MQUEUE=y
CONFIG_POSIX_MQUEUE_SYSCTL=y
CONFIG_WATCH_QUEUE=y
CONFIG_CROSS_MEMORY_ATTACH=y
CONFIG_USELIB=y
CONFIG_AUDIT=y
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
CONFIG_AUDITSYSCALL=y

#
# IRQ subsystem
#
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_IRQ_INJECTION=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_SIM=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS=y
CONFIG_GENERIC_MSI_IRQ=y
CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
CONFIG_SPARSE_IRQ=y
CONFIG_GENERIC_IRQ_DEBUGFS=y
# end of IRQ subsystem

CONFIG_ARCH_CLOCKSOURCE_DATA=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_TIME_KUNIT_TEST=y
CONFIG_CONTEXT_TRACKING=y
CONFIG_CONTEXT_TRACKING_IDLE=y

#
# Timers subsystem
#
CONFIG_TICK_ONESHOT=y
CONFIG_NO_HZ_COMMON=y
# CONFIG_HZ_PERIODIC is not set
CONFIG_NO_HZ_IDLE=y
# CONFIG_NO_HZ_FULL is not set
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
# end of Timers subsystem

CONFIG_BPF=y
CONFIG_HAVE_EBPF_JIT=y

#
# BPF subsystem
#
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_BPF_JIT_DEFAULT_ON=y
CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
CONFIG_USERMODE_DRIVER=y
CONFIG_BPF_LSM=y
# end of BPF subsystem

CONFIG_PREEMPT_NONE_BUILD=y
CONFIG_PREEMPT_NONE=y
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT is not set
CONFIG_PREEMPT_COUNT=y
CONFIG_SCHED_CORE=y

#
# CPU/Task time and stats accounting
#
CONFIG_TICK_CPU_ACCOUNTING=y
# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_BSD_PROCESS_ACCT_V3=y
CONFIG_TASKSTATS=y
CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_PSI=y
CONFIG_PSI_DEFAULT_DISABLED=y
# end of CPU/Task time and stats accounting

CONFIG_CPU_ISOLATION=y

#
# RCU Subsystem
#
CONFIG_TREE_RCU=y
CONFIG_RCU_EXPERT=y
CONFIG_SRCU=y
CONFIG_TREE_SRCU=y
CONFIG_TASKS_RCU_GENERIC=y
CONFIG_FORCE_TASKS_RCU=y
CONFIG_TASKS_RCU=y
CONFIG_FORCE_TASKS_RUDE_RCU=y
CONFIG_TASKS_RUDE_RCU=y
CONFIG_FORCE_TASKS_TRACE_RCU=y
CONFIG_TASKS_TRACE_RCU=y
CONFIG_RCU_STALL_COMMON=y
CONFIG_RCU_NEED_SEGCBLIST=y
CONFIG_RCU_FANOUT=64
CONFIG_RCU_FANOUT_LEAF=16
CONFIG_RCU_NOCB_CPU=y
CONFIG_RCU_NOCB_CPU_DEFAULT_ALL=y
CONFIG_TASKS_TRACE_RCU_READ_MB=y
# end of RCU Subsystem

CONFIG_BUILD_BIN2C=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_IKHEADERS=y
CONFIG_LOG_BUF_SHIFT=17
CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13
CONFIG_PRINTK_INDEX=y

#
# Scheduler features
#
CONFIG_UCLAMP_TASK=y
CONFIG_UCLAMP_BUCKETS_COUNT=5
# end of Scheduler features

CONFIG_CC_HAS_INT128=y
CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5"
CONFIG_GCC12_NO_ARRAY_BOUNDS=y
CONFIG_CC_NO_ARRAY_BOUNDS=y
CONFIG_CGROUPS=y
CONFIG_PAGE_COUNTER=y
CONFIG_CGROUP_FAVOR_DYNMODS=y
CONFIG_MEMCG=y
CONFIG_MEMCG_KMEM=y
CONFIG_BLK_CGROUP=y
CONFIG_CGROUP_WRITEBACK=y
CONFIG_CGROUP_SCHED=y
CONFIG_FAIR_GROUP_SCHED=y
CONFIG_CFS_BANDWIDTH=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_UCLAMP_TASK_GROUP=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_RDMA=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_CPUSETS=y
CONFIG_PROC_PID_CPUSET=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_BPF=y
CONFIG_CGROUP_MISC=y
CONFIG_CGROUP_DEBUG=y
CONFIG_SOCK_CGROUP_DATA=y
CONFIG_NAMESPACES=y
CONFIG_UTS_NS=y
CONFIG_IPC_NS=y
CONFIG_USER_NS=y
CONFIG_PID_NS=y
CONFIG_NET_NS=y
CONFIG_CHECKPOINT_RESTORE=y
CONFIG_SCHED_AUTOGROUP=y
CONFIG_SYSFS_DEPRECATED=y
CONFIG_SYSFS_DEPRECATED_V2=y
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_RD_GZIP=y
CONFIG_RD_BZIP2=y
CONFIG_RD_LZMA=y
CONFIG_RD_XZ=y
CONFIG_RD_LZO=y
CONFIG_RD_LZ4=y
CONFIG_RD_ZSTD=y
CONFIG_BOOT_CONFIG=y
CONFIG_BOOT_CONFIG_EMBED=y
CONFIG_BOOT_CONFIG_EMBED_FILE=""
CONFIG_INITRAMFS_PRESERVE_MTIME=y
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_SYSCTL=y
CONFIG_HAVE_UID16=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
CONFIG_EXPERT=y
CONFIG_UID16=y
CONFIG_MULTIUSER=y
CONFIG_SGETMASK_SYSCALL=y
CONFIG_SYSFS_SYSCALL=y
CONFIG_FHANDLE=y
CONFIG_POSIX_TIMERS=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_FUTEX_PI=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
CONFIG_IO_URING=y
CONFIG_ADVISE_SYSCALLS=y
CONFIG_MEMBARRIER=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KALLSYMS_BASE_RELATIVE=y
CONFIG_KCMP=y
CONFIG_EMBEDDED=y
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PC104=y

#
# Kernel Performance Events And Counters
#
CONFIG_PERF_EVENTS=y
CONFIG_DEBUG_PERF_USE_VMALLOC=y
# end of Kernel Performance Events And Counters

CONFIG_SYSTEM_DATA_VERIFICATION=y
CONFIG_PROFILING=y
CONFIG_TRACEPOINTS=y
# end of General setup

CONFIG_64BIT=y
CONFIG_SPARC=y
CONFIG_SPARC64=y
CONFIG_ARCH_PROC_KCORE_TEXT=y
CONFIG_CPU_BIG_ENDIAN=y
CONFIG_ARCH_ATU=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_AUDIT_ARCH=y
CONFIG_MMU=y
CONFIG_PGTABLE_LEVELS=4
CONFIG_ARCH_SUPPORTS_UPROBES=y

#
# Processor type and features
#
CONFIG_SMP=y
CONFIG_NR_CPUS=4096
# CONFIG_HZ_100 is not set
CONFIG_HZ_250=y
# CONFIG_HZ_300 is not set
# CONFIG_HZ_1000 is not set
CONFIG_HZ=250
CONFIG_SCHED_HRTICK=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_ARCH_MAY_HAVE_PC_FDC=y
CONFIG_SPARC64_SMP=y
CONFIG_EARLYFB=y
CONFIG_HOTPLUG_CPU=y

#
# CPU Frequency scaling
#
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_ATTR_SET=y
CONFIG_CPU_FREQ_GOV_COMMON=y
CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y

#
# CPU frequency scaling drivers
#
CONFIG_CPUFREQ_DT=y
CONFIG_CPUFREQ_DT_PLATDEV=y
CONFIG_SPARC_US3_CPUFREQ=y
CONFIG_SPARC_US2E_CPUFREQ=y
CONFIG_QORIQ_CPUFREQ=y
# end of CPU Frequency scaling

CONFIG_US3_MC=y
CONFIG_NUMA=y
CONFIG_NODES_SHIFT=5
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_DEFAULT=y
CONFIG_ARCH_FORCE_MAX_ORDER=13
CONFIG_HIBERNATE_CALLBACKS=y
CONFIG_HIBERNATION=y
CONFIG_HIBERNATION_SNAPSHOT_DEV=y
CONFIG_PM_STD_PARTITION=""
CONFIG_PM_SLEEP=y
CONFIG_PM_SLEEP_SMP=y
CONFIG_PM_AUTOSLEEP=y
CONFIG_PM_USERSPACE_AUTOSLEEP=y
CONFIG_PM_WAKELOCKS=y
CONFIG_PM_WAKELOCKS_LIMIT=100
CONFIG_PM_WAKELOCKS_GC=y
CONFIG_PM=y
CONFIG_PM_DEBUG=y
CONFIG_PM_ADVANCED_DEBUG=y
CONFIG_PM_SLEEP_DEBUG=y
CONFIG_DPM_WATCHDOG=y
CONFIG_DPM_WATCHDOG_TIMEOUT=120
CONFIG_PM_CLK=y
CONFIG_PM_GENERIC_DOMAINS=y
CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y
CONFIG_PM_GENERIC_DOMAINS_SLEEP=y
CONFIG_PM_GENERIC_DOMAINS_OF=y
CONFIG_ENERGY_MODEL=y
CONFIG_SCHED_SMT=y
CONFIG_SCHED_MC=y
CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="console=ttyS0,9600 root=/dev/sda1"
# end of Processor type and features

#
# Bus options (PCI etc.)
#
CONFIG_SBUS=y
CONFIG_SBUSCHAR=y
CONFIG_SUN_LDOMS=y
CONFIG_SUN_OPENPROMFS=y
CONFIG_SPARC64_PCI=y
CONFIG_SPARC64_PCI_MSI=y
# end of Bus options (PCI etc.)

CONFIG_COMPAT=y

#
# Misc Linux/SPARC drivers
#
CONFIG_SUN_OPENPROMIO=y
CONFIG_OBP_FLASH=y
CONFIG_TADPOLE_TS102_UCTRL=y
CONFIG_BBC_I2C=y
CONFIG_ENVCTRL=y
CONFIG_DISPLAY7SEG=y
CONFIG_ORACLE_DAX=y
# end of Misc Linux/SPARC drivers

#
# General architecture-dependent options
#
CONFIG_CRASH_CORE=y
CONFIG_KPROBES=y
CONFIG_JUMP_LABEL=y
CONFIG_STATIC_KEYS_SELFTEST=y
CONFIG_UPROBES=y
CONFIG_HAVE_64BIT_ALIGNED_ACCESS=y
CONFIG_KRETPROBES=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_NMI=y
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_HAVE_ASM_MODVERSIONS=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_NMI_WATCHDOG=y
CONFIG_HAVE_ARCH_JUMP_LABEL=y
CONFIG_MMU_GATHER_TABLE_FREE=y
CONFIG_MMU_GATHER_RCU_TABLE_FREE=y
CONFIG_MMU_GATHER_NO_FLUSH_CACHE=y
CONFIG_MMU_GATHER_MERGE_VMAS=y
CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y
CONFIG_ARCH_WANT_OLD_COMPAT_IPC=y
CONFIG_HAVE_ARCH_SECCOMP=y
CONFIG_SECCOMP=y
CONFIG_LTO_NONE=y
CONFIG_HAVE_CONTEXT_TRACKING_USER=y
CONFIG_HAVE_TIF_NOHZ=y
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
CONFIG_MODULES_USE_ELF_RELA=y
CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK=y
CONFIG_SOFTIRQ_ON_OWN_STACK=y
CONFIG_ALTERNATE_USER_ADDRESS_SPACE=y
CONFIG_HAVE_EXIT_THREAD=y
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
CONFIG_ISA_BUS_API=y
CONFIG_ODD_RT_SIGACTION=y
CONFIG_OLD_SIGSUSPEND=y
CONFIG_COMPAT_OLD_SIGACTION=y
CONFIG_COMPAT_32BIT_TIME=y
CONFIG_CPU_NO_EFFICIENT_FFS=y
CONFIG_LOCK_EVENT_COUNTS=y
CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y

#
# GCOV-based kernel profiling
#
CONFIG_GCOV_KERNEL=y
# end of GCOV-based kernel profiling
# end of General architecture-dependent options

CONFIG_RT_MUTEXES=y
CONFIG_BASE_SMALL=0
CONFIG_MODULE_SIG_FORMAT=y
CONFIG_MODULES=y
CONFIG_MODULE_FORCE_LOAD=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODULE_UNLOAD_TAINT_TRACKING=y
CONFIG_MODVERSIONS=y
CONFIG_ASM_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_FORCE=y
CONFIG_MODULE_SIG_ALL=y
CONFIG_MODULE_SIG_SHA1=y
# CONFIG_MODULE_SIG_SHA224 is not set
# CONFIG_MODULE_SIG_SHA256 is not set
# CONFIG_MODULE_SIG_SHA384 is not set
# CONFIG_MODULE_SIG_SHA512 is not set
CONFIG_MODULE_SIG_HASH="sha1"
CONFIG_MODULE_COMPRESS_NONE=y
# CONFIG_MODULE_COMPRESS_GZIP is not set
# CONFIG_MODULE_COMPRESS_XZ is not set
# CONFIG_MODULE_COMPRESS_ZSTD is not set
CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS=y
CONFIG_MODPROBE_PATH="/sbin/modprobe"
CONFIG_MODULES_TREE_LOOKUP=y
CONFIG_BLOCK=y
CONFIG_BLOCK_LEGACY_AUTOLOAD=y
CONFIG_BLK_RQ_ALLOC_TIME=y
CONFIG_BLK_CGROUP_RWSTAT=y
CONFIG_BLK_DEV_BSG_COMMON=y
CONFIG_BLK_ICQ=y
CONFIG_BLK_DEV_BSGLIB=y
CONFIG_BLK_DEV_INTEGRITY=y
CONFIG_BLK_DEV_INTEGRITY_T10=y
CONFIG_BLK_DEV_ZONED=y
CONFIG_BLK_DEV_THROTTLING=y
CONFIG_BLK_DEV_THROTTLING_LOW=y
CONFIG_BLK_WBT=y
CONFIG_BLK_WBT_MQ=y
CONFIG_BLK_CGROUP_IOLATENCY=y
CONFIG_BLK_CGROUP_FC_APPID=y
CONFIG_BLK_CGROUP_IOCOST=y
CONFIG_BLK_CGROUP_IOPRIO=y
CONFIG_BLK_DEBUG_FS=y
CONFIG_BLK_DEBUG_FS_ZONED=y
CONFIG_BLK_SED_OPAL=y
CONFIG_BLK_INLINE_ENCRYPTION=y
CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y

#
# Partition Types
#
CONFIG_PARTITION_ADVANCED=y
CONFIG_ACORN_PARTITION=y
CONFIG_ACORN_PARTITION_CUMANA=y
CONFIG_ACORN_PARTITION_EESOX=y
CONFIG_ACORN_PARTITION_ICS=y
CONFIG_ACORN_PARTITION_ADFS=y
CONFIG_ACORN_PARTITION_POWERTEC=y
CONFIG_ACORN_PARTITION_RISCIX=y
CONFIG_AIX_PARTITION=y
CONFIG_OSF_PARTITION=y
CONFIG_AMIGA_PARTITION=y
CONFIG_ATARI_PARTITION=y
CONFIG_MAC_PARTITION=y
CONFIG_MSDOS_PARTITION=y
CONFIG_BSD_DISKLABEL=y
CONFIG_MINIX_SUBPARTITION=y
CONFIG_SOLARIS_X86_PARTITION=y
CONFIG_UNIXWARE_DISKLABEL=y
CONFIG_LDM_PARTITION=y
CONFIG_LDM_DEBUG=y
CONFIG_SGI_PARTITION=y
CONFIG_ULTRIX_PARTITION=y
CONFIG_SUN_PARTITION=y
CONFIG_KARMA_PARTITION=y
CONFIG_EFI_PARTITION=y
CONFIG_SYSV68_PARTITION=y
CONFIG_CMDLINE_PARTITION=y
# end of Partition Types

CONFIG_BLOCK_COMPAT=y
CONFIG_BLK_MQ_PCI=y
CONFIG_BLK_MQ_VIRTIO=y
CONFIG_BLK_MQ_RDMA=y
CONFIG_BLK_PM=y
CONFIG_BLOCK_HOLDER_DEPRECATED=y
CONFIG_BLK_MQ_STACKING=y

#
# IO Schedulers
#
CONFIG_MQ_IOSCHED_DEADLINE=y
CONFIG_MQ_IOSCHED_KYBER=y
CONFIG_IOSCHED_BFQ=y
CONFIG_BFQ_GROUP_IOSCHED=y
CONFIG_BFQ_CGROUP_DEBUG=y
# end of IO Schedulers

CONFIG_PADATA=y
CONFIG_ASN1=y
CONFIG_UNINLINE_SPIN_UNLOCK=y
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_RWSEM_SPIN_ON_OWNER=y
CONFIG_LOCK_SPIN_ON_OWNER=y
CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_FREEZER=y

#
# Executable file formats
#
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_ELF_KUNIT_TEST=y
CONFIG_COMPAT_BINFMT_ELF=y
CONFIG_ELFCORE=y
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
CONFIG_BINFMT_SCRIPT=y
CONFIG_BINFMT_MISC=y
CONFIG_COREDUMP=y
# end of Executable file formats

#
# Memory Management options
#
CONFIG_ZPOOL=y
CONFIG_SWAP=y
CONFIG_ZSWAP=y
CONFIG_ZSWAP_DEFAULT_ON=y
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_DEFLATE is not set
CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO=y
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4 is not set
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4HC is not set
# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD is not set
CONFIG_ZSWAP_COMPRESSOR_DEFAULT="lzo"
CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y
# CONFIG_ZSWAP_ZPOOL_DEFAULT_Z3FOLD is not set
# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set
CONFIG_ZSWAP_ZPOOL_DEFAULT="zbud"
CONFIG_ZBUD=y
CONFIG_Z3FOLD=y
CONFIG_ZSMALLOC=y
CONFIG_ZSMALLOC_STAT=y

#
# SLAB allocator options
#
# CONFIG_SLAB is not set
CONFIG_SLUB=y
# CONFIG_SLOB is not set
CONFIG_SLAB_MERGE_DEFAULT=y
CONFIG_SLAB_FREELIST_RANDOM=y
CONFIG_SLAB_FREELIST_HARDENED=y
CONFIG_SLUB_STATS=y
CONFIG_SLUB_CPU_PARTIAL=y
# end of SLAB allocator options

CONFIG_SHUFFLE_PAGE_ALLOCATOR=y
CONFIG_COMPAT_BRK=y
CONFIG_SPARSEMEM=y
CONFIG_SPARSEMEM_EXTREME=y
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
CONFIG_SPARSEMEM_VMEMMAP=y
CONFIG_MEMORY_ISOLATION=y
CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_MEMORY_BALLOON=y
CONFIG_BALLOON_COMPACTION=y
CONFIG_COMPACTION=y
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
CONFIG_PAGE_REPORTING=y
CONFIG_MIGRATION=y
CONFIG_CONTIG_ALLOC=y
CONFIG_PHYS_ADDR_T_64BIT=y
CONFIG_MMU_NOTIFIER=y
CONFIG_KSM=y
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set
CONFIG_READ_ONLY_THP_FOR_FS=y
CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
CONFIG_HAVE_SETUP_PER_CPU_AREA=y
CONFIG_FRONTSWAP=y
CONFIG_CMA=y
CONFIG_CMA_DEBUG=y
CONFIG_CMA_DEBUGFS=y
CONFIG_CMA_SYSFS=y
CONFIG_CMA_AREAS=19
CONFIG_DEFERRED_STRUCT_PAGE_INIT=y
CONFIG_PAGE_IDLE_FLAG=y
CONFIG_IDLE_PAGE_TRACKING=y
CONFIG_HMM_MIRROR=y
CONFIG_GET_FREE_REGION=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_PERCPU_STATS=y
CONFIG_GUP_TEST=y
CONFIG_ARCH_HAS_PTE_SPECIAL=y
CONFIG_ANON_VMA_NAME=y
CONFIG_USERFAULTFD=y
CONFIG_LRU_GEN=y
CONFIG_LRU_GEN_ENABLED=y
CONFIG_LRU_GEN_STATS=y

#
# Data Access Monitoring
#
CONFIG_DAMON=y
CONFIG_DAMON_KUNIT_TEST=y
CONFIG_DAMON_VADDR=y
CONFIG_DAMON_PADDR=y
CONFIG_DAMON_VADDR_KUNIT_TEST=y
CONFIG_DAMON_SYSFS=y
CONFIG_DAMON_DBGFS=y
CONFIG_DAMON_DBGFS_KUNIT_TEST=y
CONFIG_DAMON_RECLAIM=y
CONFIG_DAMON_LRU_SORT=y
# end of Data Access Monitoring
# end of Memory Management options

CONFIG_NET=y
CONFIG_COMPAT_NETLINK_MESSAGES=y
CONFIG_NET_INGRESS=y
CONFIG_NET_EGRESS=y
CONFIG_NET_REDIRECT=y
CONFIG_SKB_EXTENSIONS=y

#
# Networking options
#
CONFIG_PACKET=y
CONFIG_PACKET_DIAG=y
CONFIG_UNIX=y
CONFIG_UNIX_SCM=y
CONFIG_AF_UNIX_OOB=y
CONFIG_UNIX_DIAG=y
CONFIG_TLS=y
CONFIG_TLS_DEVICE=y
CONFIG_TLS_TOE=y
CONFIG_XFRM=y
CONFIG_XFRM_OFFLOAD=y
CONFIG_XFRM_ALGO=y
CONFIG_XFRM_USER=y
CONFIG_XFRM_INTERFACE=y
CONFIG_XFRM_SUB_POLICY=y
CONFIG_XFRM_MIGRATE=y
CONFIG_XFRM_STATISTICS=y
CONFIG_XFRM_AH=y
CONFIG_XFRM_ESP=y
CONFIG_XFRM_IPCOMP=y
CONFIG_NET_KEY=y
CONFIG_NET_KEY_MIGRATE=y
CONFIG_XFRM_ESPINTCP=y
CONFIG_SMC=y
CONFIG_SMC_DIAG=y
CONFIG_XDP_SOCKETS=y
CONFIG_XDP_SOCKETS_DIAG=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_FIB_TRIE_STATS=y
CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_ROUTE_MULTIPATH=y
CONFIG_IP_ROUTE_VERBOSE=y
CONFIG_IP_ROUTE_CLASSID=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IP_PNP_RARP=y
CONFIG_NET_IPIP=y
CONFIG_NET_IPGRE_DEMUX=y
CONFIG_NET_IP_TUNNEL=y
CONFIG_NET_IPGRE=y
CONFIG_NET_IPGRE_BROADCAST=y
CONFIG_IP_MROUTE_COMMON=y
CONFIG_IP_MROUTE=y
CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
CONFIG_IP_PIMSM_V1=y
CONFIG_IP_PIMSM_V2=y
CONFIG_SYN_COOKIES=y
CONFIG_NET_IPVTI=y
CONFIG_NET_UDP_TUNNEL=y
CONFIG_NET_FOU=y
CONFIG_NET_FOU_IP_TUNNELS=y
CONFIG_INET_AH=y
CONFIG_INET_ESP=y
CONFIG_INET_ESP_OFFLOAD=y
CONFIG_INET_ESPINTCP=y
CONFIG_INET_IPCOMP=y
CONFIG_INET_TABLE_PERTURB_ORDER=16
CONFIG_INET_XFRM_TUNNEL=y
CONFIG_INET_TUNNEL=y
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
CONFIG_INET_UDP_DIAG=y
CONFIG_INET_RAW_DIAG=y
CONFIG_INET_DIAG_DESTROY=y
CONFIG_TCP_CONG_ADVANCED=y
CONFIG_TCP_CONG_BIC=y
CONFIG_TCP_CONG_CUBIC=y
CONFIG_TCP_CONG_WESTWOOD=y
CONFIG_TCP_CONG_HTCP=y
CONFIG_TCP_CONG_HSTCP=y
CONFIG_TCP_CONG_HYBLA=y
CONFIG_TCP_CONG_VEGAS=y
CONFIG_TCP_CONG_NV=y
CONFIG_TCP_CONG_SCALABLE=y
CONFIG_TCP_CONG_LP=y
CONFIG_TCP_CONG_VENO=y
CONFIG_TCP_CONG_YEAH=y
CONFIG_TCP_CONG_ILLINOIS=y
CONFIG_TCP_CONG_DCTCP=y
CONFIG_TCP_CONG_CDG=y
CONFIG_TCP_CONG_BBR=y
# CONFIG_DEFAULT_BIC is not set
CONFIG_DEFAULT_CUBIC=y
# CONFIG_DEFAULT_HTCP is not set
# CONFIG_DEFAULT_HYBLA is not set
# CONFIG_DEFAULT_VEGAS is not set
# CONFIG_DEFAULT_VENO is not set
# CONFIG_DEFAULT_WESTWOOD is not set
# CONFIG_DEFAULT_DCTCP is not set
# CONFIG_DEFAULT_CDG is not set
# CONFIG_DEFAULT_BBR is not set
# CONFIG_DEFAULT_RENO is not set
CONFIG_DEFAULT_TCP_CONG="cubic"
CONFIG_TCP_MD5SIG=y
CONFIG_IPV6=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
CONFIG_INET6_AH=y
CONFIG_INET6_ESP=y
CONFIG_INET6_ESP_OFFLOAD=y
CONFIG_INET6_ESPINTCP=y
CONFIG_INET6_IPCOMP=y
CONFIG_IPV6_MIP6=y
CONFIG_IPV6_ILA=y
CONFIG_INET6_XFRM_TUNNEL=y
CONFIG_INET6_TUNNEL=y
CONFIG_IPV6_VTI=y
CONFIG_IPV6_SIT=y
CONFIG_IPV6_SIT_6RD=y
CONFIG_IPV6_NDISC_NODETYPE=y
CONFIG_IPV6_TUNNEL=y
CONFIG_IPV6_GRE=y
CONFIG_IPV6_FOU=y
CONFIG_IPV6_FOU_TUNNEL=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_IPV6_MROUTE=y
CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
CONFIG_IPV6_PIMSM_V2=y
CONFIG_IPV6_SEG6_LWTUNNEL=y
CONFIG_IPV6_SEG6_HMAC=y
CONFIG_IPV6_SEG6_BPF=y
CONFIG_IPV6_RPL_LWTUNNEL=y
CONFIG_IPV6_IOAM6_LWTUNNEL=y
CONFIG_NETLABEL=y
CONFIG_MPTCP=y
CONFIG_INET_MPTCP_DIAG=y
CONFIG_MPTCP_IPV6=y
CONFIG_MPTCP_KUNIT_TEST=y
CONFIG_NETWORK_SECMARK=y
CONFIG_NET_PTP_CLASSIFY=y
CONFIG_NETWORK_PHY_TIMESTAMPING=y
CONFIG_NETFILTER=y
CONFIG_NETFILTER_ADVANCED=y
CONFIG_BRIDGE_NETFILTER=y

#
# Core Netfilter Configuration
#
CONFIG_NETFILTER_INGRESS=y
CONFIG_NETFILTER_EGRESS=y
CONFIG_NETFILTER_SKIP_EGRESS=y
CONFIG_NETFILTER_NETLINK=y
CONFIG_NETFILTER_FAMILY_BRIDGE=y
CONFIG_NETFILTER_FAMILY_ARP=y
CONFIG_NETFILTER_NETLINK_HOOK=y
CONFIG_NETFILTER_NETLINK_ACCT=y
CONFIG_NETFILTER_NETLINK_QUEUE=y
CONFIG_NETFILTER_NETLINK_LOG=y
CONFIG_NETFILTER_NETLINK_OSF=y
CONFIG_NF_CONNTRACK=y
CONFIG_NF_LOG_SYSLOG=y
CONFIG_NETFILTER_CONNCOUNT=y
CONFIG_NF_CONNTRACK_MARK=y
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_ZONES=y
CONFIG_NF_CONNTRACK_PROCFS=y
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NF_CONNTRACK_TIMEOUT=y
CONFIG_NF_CONNTRACK_TIMESTAMP=y
CONFIG_NF_CONNTRACK_LABELS=y
CONFIG_NF_CT_PROTO_DCCP=y
CONFIG_NF_CT_PROTO_GRE=y
CONFIG_NF_CT_PROTO_SCTP=y
CONFIG_NF_CT_PROTO_UDPLITE=y
CONFIG_NF_CONNTRACK_AMANDA=y
CONFIG_NF_CONNTRACK_FTP=y
CONFIG_NF_CONNTRACK_H323=y
CONFIG_NF_CONNTRACK_IRC=y
CONFIG_NF_CONNTRACK_BROADCAST=y
CONFIG_NF_CONNTRACK_NETBIOS_NS=y
CONFIG_NF_CONNTRACK_SNMP=y
CONFIG_NF_CONNTRACK_PPTP=y
CONFIG_NF_CONNTRACK_SANE=y
CONFIG_NF_CONNTRACK_SIP=y
CONFIG_NF_CONNTRACK_TFTP=y
CONFIG_NF_CT_NETLINK=y
CONFIG_NF_CT_NETLINK_TIMEOUT=y
CONFIG_NF_CT_NETLINK_HELPER=y
CONFIG_NETFILTER_NETLINK_GLUE_CT=y
CONFIG_NF_NAT=y
CONFIG_NF_NAT_AMANDA=y
CONFIG_NF_NAT_FTP=y
CONFIG_NF_NAT_IRC=y
CONFIG_NF_NAT_SIP=y
CONFIG_NF_NAT_TFTP=y
CONFIG_NF_NAT_REDIRECT=y
CONFIG_NF_NAT_MASQUERADE=y
CONFIG_NETFILTER_SYNPROXY=y
CONFIG_NF_TABLES=y
CONFIG_NF_TABLES_INET=y
CONFIG_NF_TABLES_NETDEV=y
CONFIG_NFT_NUMGEN=y
CONFIG_NFT_CT=y
CONFIG_NFT_FLOW_OFFLOAD=y
CONFIG_NFT_CONNLIMIT=y
CONFIG_NFT_LOG=y
CONFIG_NFT_LIMIT=y
CONFIG_NFT_MASQ=y
CONFIG_NFT_REDIR=y
CONFIG_NFT_NAT=y
CONFIG_NFT_TUNNEL=y
# CONFIG_NFT_OBJREF is not set
CONFIG_NFT_QUEUE=y
CONFIG_NFT_QUOTA=y
CONFIG_NFT_REJECT=y
CONFIG_NFT_REJECT_INET=y
CONFIG_NFT_COMPAT=y
CONFIG_NFT_HASH=y
CONFIG_NFT_FIB=y
CONFIG_NFT_FIB_INET=y
CONFIG_NFT_XFRM=y
CONFIG_NFT_SOCKET=y
CONFIG_NFT_OSF=y
CONFIG_NFT_TPROXY=y
CONFIG_NFT_SYNPROXY=y
CONFIG_NF_DUP_NETDEV=y
CONFIG_NFT_DUP_NETDEV=y
CONFIG_NFT_FWD_NETDEV=y
CONFIG_NFT_FIB_NETDEV=y
CONFIG_NFT_REJECT_NETDEV=y
CONFIG_NF_FLOW_TABLE_INET=y
CONFIG_NF_FLOW_TABLE=y
CONFIG_NF_FLOW_TABLE_PROCFS=y
CONFIG_NETFILTER_XTABLES=y
CONFIG_NETFILTER_XTABLES_COMPAT=y

#
# Xtables combined modules
#
CONFIG_NETFILTER_XT_MARK=y
CONFIG_NETFILTER_XT_CONNMARK=y
CONFIG_NETFILTER_XT_SET=y

#
# Xtables targets
#
CONFIG_NETFILTER_XT_TARGET_AUDIT=y
CONFIG_NETFILTER_XT_TARGET_CHECKSUM=y
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
CONFIG_NETFILTER_XT_TARGET_CT=y
CONFIG_NETFILTER_XT_TARGET_DSCP=y
CONFIG_NETFILTER_XT_TARGET_HL=y
CONFIG_NETFILTER_XT_TARGET_HMARK=y
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
CONFIG_NETFILTER_XT_TARGET_LED=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_NAT=y
CONFIG_NETFILTER_XT_TARGET_NETMAP=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
CONFIG_NETFILTER_XT_TARGET_RATEEST=y
CONFIG_NETFILTER_XT_TARGET_REDIRECT=y
CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
CONFIG_NETFILTER_XT_TARGET_TEE=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=y

#
# Xtables matches
#
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NETFILTER_XT_MATCH_CGROUP=y
CONFIG_NETFILTER_XT_MATCH_CLUSTER=y
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_CPU=y
CONFIG_NETFILTER_XT_MATCH_DCCP=y
CONFIG_NETFILTER_XT_MATCH_DEVGROUP=y
CONFIG_NETFILTER_XT_MATCH_DSCP=y
CONFIG_NETFILTER_XT_MATCH_ECN=y
CONFIG_NETFILTER_XT_MATCH_ESP=y
CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
CONFIG_NETFILTER_XT_MATCH_HELPER=y
CONFIG_NETFILTER_XT_MATCH_HL=y
CONFIG_NETFILTER_XT_MATCH_IPCOMP=y
CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
CONFIG_NETFILTER_XT_MATCH_IPVS=y
CONFIG_NETFILTER_XT_MATCH_L2TP=y
CONFIG_NETFILTER_XT_MATCH_LENGTH=y
CONFIG_NETFILTER_XT_MATCH_LIMIT=y
CONFIG_NETFILTER_XT_MATCH_MAC=y
CONFIG_NETFILTER_XT_MATCH_MARK=y
CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
CONFIG_NETFILTER_XT_MATCH_NFACCT=y
CONFIG_NETFILTER_XT_MATCH_OSF=y
CONFIG_NETFILTER_XT_MATCH_OWNER=y
CONFIG_NETFILTER_XT_MATCH_POLICY=y
CONFIG_NETFILTER_XT_MATCH_PHYSDEV=y
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
CONFIG_NETFILTER_XT_MATCH_RATEEST=y
CONFIG_NETFILTER_XT_MATCH_REALM=y
CONFIG_NETFILTER_XT_MATCH_RECENT=y
CONFIG_NETFILTER_XT_MATCH_SCTP=y
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
CONFIG_NETFILTER_XT_MATCH_STATE=y
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
CONFIG_NETFILTER_XT_MATCH_STRING=y
CONFIG_NETFILTER_XT_MATCH_TCPMSS=y
CONFIG_NETFILTER_XT_MATCH_TIME=y
CONFIG_NETFILTER_XT_MATCH_U32=y
# end of Core Netfilter Configuration

CONFIG_IP_SET=y
CONFIG_IP_SET_MAX=256
CONFIG_IP_SET_BITMAP_IP=y
CONFIG_IP_SET_BITMAP_IPMAC=y
CONFIG_IP_SET_BITMAP_PORT=y
CONFIG_IP_SET_HASH_IP=y
CONFIG_IP_SET_HASH_IPMARK=y
CONFIG_IP_SET_HASH_IPPORT=y
CONFIG_IP_SET_HASH_IPPORTIP=y
CONFIG_IP_SET_HASH_IPPORTNET=y
CONFIG_IP_SET_HASH_IPMAC=y
CONFIG_IP_SET_HASH_MAC=y
CONFIG_IP_SET_HASH_NETPORTNET=y
CONFIG_IP_SET_HASH_NET=y
CONFIG_IP_SET_HASH_NETNET=y
CONFIG_IP_SET_HASH_NETPORT=y
CONFIG_IP_SET_HASH_NETIFACE=y
CONFIG_IP_SET_LIST_SET=y
CONFIG_IP_VS=y
CONFIG_IP_VS_IPV6=y
CONFIG_IP_VS_DEBUG=y
CONFIG_IP_VS_TAB_BITS=12

#
# IPVS transport protocol load balancing support
#
CONFIG_IP_VS_PROTO_TCP=y
CONFIG_IP_VS_PROTO_UDP=y
CONFIG_IP_VS_PROTO_AH_ESP=y
CONFIG_IP_VS_PROTO_ESP=y
CONFIG_IP_VS_PROTO_AH=y
CONFIG_IP_VS_PROTO_SCTP=y

#
# IPVS scheduler
#
CONFIG_IP_VS_RR=y
CONFIG_IP_VS_WRR=y
CONFIG_IP_VS_LC=y
CONFIG_IP_VS_WLC=y
CONFIG_IP_VS_FO=y
CONFIG_IP_VS_OVF=y
CONFIG_IP_VS_LBLC=y
CONFIG_IP_VS_LBLCR=y
CONFIG_IP_VS_DH=y
CONFIG_IP_VS_SH=y
CONFIG_IP_VS_MH=y
CONFIG_IP_VS_SED=y
CONFIG_IP_VS_NQ=y
CONFIG_IP_VS_TWOS=y

#
# IPVS SH scheduler
#
CONFIG_IP_VS_SH_TAB_BITS=8

#
# IPVS MH scheduler
#
CONFIG_IP_VS_MH_TAB_INDEX=12

#
# IPVS application helper
#
CONFIG_IP_VS_FTP=y
CONFIG_IP_VS_NFCT=y
CONFIG_IP_VS_PE_SIP=y

#
# IP: Netfilter Configuration
#
CONFIG_NF_DEFRAG_IPV4=y
CONFIG_NF_SOCKET_IPV4=y
CONFIG_NF_TPROXY_IPV4=y
CONFIG_NF_TABLES_IPV4=y
CONFIG_NFT_REJECT_IPV4=y
CONFIG_NFT_DUP_IPV4=y
CONFIG_NFT_FIB_IPV4=y
CONFIG_NF_TABLES_ARP=y
CONFIG_NF_DUP_IPV4=y
CONFIG_NF_LOG_ARP=y
CONFIG_NF_LOG_IPV4=y
CONFIG_NF_REJECT_IPV4=y
CONFIG_NF_NAT_SNMP_BASIC=y
CONFIG_NF_NAT_PPTP=y
CONFIG_NF_NAT_H323=y
CONFIG_IP_NF_IPTABLES=y
CONFIG_IP_NF_MATCH_AH=y
CONFIG_IP_NF_MATCH_ECN=y
CONFIG_IP_NF_MATCH_RPFILTER=y
CONFIG_IP_NF_MATCH_TTL=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_REJECT=y
CONFIG_IP_NF_TARGET_SYNPROXY=y
CONFIG_IP_NF_NAT=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
CONFIG_IP_NF_TARGET_NETMAP=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
CONFIG_IP_NF_TARGET_CLUSTERIP=y
CONFIG_IP_NF_TARGET_ECN=y
CONFIG_IP_NF_TARGET_TTL=y
CONFIG_IP_NF_RAW=y
CONFIG_IP_NF_SECURITY=y
CONFIG_IP_NF_ARPTABLES=y
CONFIG_IP_NF_ARPFILTER=y
CONFIG_IP_NF_ARP_MANGLE=y
# end of IP: Netfilter Configuration

#
# IPv6: Netfilter Configuration
#
CONFIG_NF_SOCKET_IPV6=y
CONFIG_NF_TPROXY_IPV6=y
CONFIG_NF_TABLES_IPV6=y
CONFIG_NFT_REJECT_IPV6=y
CONFIG_NFT_DUP_IPV6=y
CONFIG_NFT_FIB_IPV6=y
CONFIG_NF_DUP_IPV6=y
CONFIG_NF_REJECT_IPV6=y
CONFIG_NF_LOG_IPV6=y
CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_MATCH_AH=y
CONFIG_IP6_NF_MATCH_EUI64=y
CONFIG_IP6_NF_MATCH_FRAG=y
CONFIG_IP6_NF_MATCH_OPTS=y
CONFIG_IP6_NF_MATCH_HL=y
CONFIG_IP6_NF_MATCH_IPV6HEADER=y
CONFIG_IP6_NF_MATCH_MH=y
CONFIG_IP6_NF_MATCH_RPFILTER=y
CONFIG_IP6_NF_MATCH_RT=y
CONFIG_IP6_NF_MATCH_SRH=y
CONFIG_IP6_NF_TARGET_HL=y
CONFIG_IP6_NF_FILTER=y
CONFIG_IP6_NF_TARGET_REJECT=y
CONFIG_IP6_NF_TARGET_SYNPROXY=y
CONFIG_IP6_NF_MANGLE=y
CONFIG_IP6_NF_RAW=y
CONFIG_IP6_NF_SECURITY=y
CONFIG_IP6_NF_NAT=y
CONFIG_IP6_NF_TARGET_MASQUERADE=y
CONFIG_IP6_NF_TARGET_NPT=y
# end of IPv6: Netfilter Configuration

CONFIG_NF_DEFRAG_IPV6=y
CONFIG_NF_TABLES_BRIDGE=y
CONFIG_NFT_BRIDGE_META=y
CONFIG_NFT_BRIDGE_REJECT=y
CONFIG_NF_CONNTRACK_BRIDGE=y
CONFIG_BRIDGE_NF_EBTABLES=y
CONFIG_BRIDGE_EBT_BROUTE=y
CONFIG_BRIDGE_EBT_T_FILTER=y
CONFIG_BRIDGE_EBT_T_NAT=y
CONFIG_BRIDGE_EBT_802_3=y
CONFIG_BRIDGE_EBT_AMONG=y
CONFIG_BRIDGE_EBT_ARP=y
CONFIG_BRIDGE_EBT_IP=y
CONFIG_BRIDGE_EBT_IP6=y
CONFIG_BRIDGE_EBT_LIMIT=y
CONFIG_BRIDGE_EBT_MARK=y
CONFIG_BRIDGE_EBT_PKTTYPE=y
CONFIG_BRIDGE_EBT_STP=y
CONFIG_BRIDGE_EBT_VLAN=y
CONFIG_BRIDGE_EBT_ARPREPLY=y
CONFIG_BRIDGE_EBT_DNAT=y
CONFIG_BRIDGE_EBT_MARK_T=y
CONFIG_BRIDGE_EBT_REDIRECT=y
CONFIG_BRIDGE_EBT_SNAT=y
CONFIG_BRIDGE_EBT_LOG=y
CONFIG_BRIDGE_EBT_NFLOG=y
CONFIG_BPFILTER=y
CONFIG_IP_DCCP=y
CONFIG_INET_DCCP_DIAG=y

#
# DCCP CCIDs Configuration
#
CONFIG_IP_DCCP_CCID2_DEBUG=y
CONFIG_IP_DCCP_CCID3=y
CONFIG_IP_DCCP_CCID3_DEBUG=y
CONFIG_IP_DCCP_TFRC_LIB=y
CONFIG_IP_DCCP_TFRC_DEBUG=y
# end of DCCP CCIDs Configuration

#
# DCCP Kernel Hacking
#
CONFIG_IP_DCCP_DEBUG=y
# end of DCCP Kernel Hacking

CONFIG_IP_SCTP=y
CONFIG_SCTP_DBG_OBJCNT=y
CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5=y
# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1 is not set
# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE is not set
CONFIG_SCTP_COOKIE_HMAC_MD5=y
CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_INET_SCTP_DIAG=y
CONFIG_RDS=y
CONFIG_RDS_RDMA=y
CONFIG_RDS_TCP=y
CONFIG_RDS_DEBUG=y
CONFIG_TIPC=y
CONFIG_TIPC_MEDIA_IB=y
CONFIG_TIPC_MEDIA_UDP=y
CONFIG_TIPC_CRYPTO=y
CONFIG_TIPC_DIAG=y
CONFIG_ATM=y
CONFIG_ATM_CLIP=y
CONFIG_ATM_CLIP_NO_ICMP=y
CONFIG_ATM_LANE=y
CONFIG_ATM_MPOA=y
CONFIG_ATM_BR2684=y
CONFIG_ATM_BR2684_IPFILTER=y
CONFIG_L2TP=y
CONFIG_L2TP_DEBUGFS=y
CONFIG_L2TP_V3=y
CONFIG_L2TP_IP=y
CONFIG_L2TP_ETH=y
CONFIG_STP=y
CONFIG_GARP=y
CONFIG_MRP=y
CONFIG_BRIDGE=y
CONFIG_BRIDGE_IGMP_SNOOPING=y
CONFIG_BRIDGE_VLAN_FILTERING=y
CONFIG_BRIDGE_MRP=y
CONFIG_BRIDGE_CFM=y
CONFIG_NET_DSA=y
CONFIG_NET_DSA_TAG_AR9331=y
CONFIG_NET_DSA_TAG_BRCM_COMMON=y
CONFIG_NET_DSA_TAG_BRCM=y
CONFIG_NET_DSA_TAG_BRCM_LEGACY=y
CONFIG_NET_DSA_TAG_BRCM_PREPEND=y
CONFIG_NET_DSA_TAG_HELLCREEK=y
CONFIG_NET_DSA_TAG_GSWIP=y
CONFIG_NET_DSA_TAG_DSA_COMMON=y
CONFIG_NET_DSA_TAG_DSA=y
CONFIG_NET_DSA_TAG_EDSA=y
CONFIG_NET_DSA_TAG_MTK=y
CONFIG_NET_DSA_TAG_KSZ=y
CONFIG_NET_DSA_TAG_OCELOT=y
CONFIG_NET_DSA_TAG_OCELOT_8021Q=y
CONFIG_NET_DSA_TAG_QCA=y
CONFIG_NET_DSA_TAG_RTL4_A=y
CONFIG_NET_DSA_TAG_RTL8_4=y
CONFIG_NET_DSA_TAG_RZN1_A5PSW=y
CONFIG_NET_DSA_TAG_LAN9303=y
CONFIG_NET_DSA_TAG_SJA1105=y
CONFIG_NET_DSA_TAG_TRAILER=y
CONFIG_NET_DSA_TAG_XRS700X=y
CONFIG_VLAN_8021Q=y
CONFIG_VLAN_8021Q_GVRP=y
CONFIG_VLAN_8021Q_MVRP=y
CONFIG_LLC=y
CONFIG_LLC2=y
CONFIG_ATALK=y
CONFIG_DEV_APPLETALK=y
CONFIG_IPDDP=y
CONFIG_IPDDP_ENCAP=y
CONFIG_X25=y
CONFIG_LAPB=y
CONFIG_PHONET=y
CONFIG_6LOWPAN=y
CONFIG_6LOWPAN_DEBUGFS=y
CONFIG_6LOWPAN_NHC=y
CONFIG_6LOWPAN_NHC_DEST=y
CONFIG_6LOWPAN_NHC_FRAGMENT=y
CONFIG_6LOWPAN_NHC_HOP=y
CONFIG_6LOWPAN_NHC_IPV6=y
CONFIG_6LOWPAN_NHC_MOBILITY=y
CONFIG_6LOWPAN_NHC_ROUTING=y
CONFIG_6LOWPAN_NHC_UDP=y
CONFIG_6LOWPAN_GHC_EXT_HDR_HOP=y
CONFIG_6LOWPAN_GHC_UDP=y
CONFIG_6LOWPAN_GHC_ICMPV6=y
CONFIG_6LOWPAN_GHC_EXT_HDR_DEST=y
CONFIG_6LOWPAN_GHC_EXT_HDR_FRAG=y
CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=y
CONFIG_IEEE802154=y
CONFIG_IEEE802154_NL802154_EXPERIMENTAL=y
CONFIG_IEEE802154_SOCKET=y
CONFIG_IEEE802154_6LOWPAN=y
CONFIG_MAC802154=y
CONFIG_NET_SCHED=y

#
# Queueing/Scheduling
#
CONFIG_NET_SCH_CBQ=y
CONFIG_NET_SCH_HTB=y
CONFIG_NET_SCH_HFSC=y
CONFIG_NET_SCH_ATM=y
CONFIG_NET_SCH_PRIO=y
CONFIG_NET_SCH_MULTIQ=y
CONFIG_NET_SCH_RED=y
CONFIG_NET_SCH_SFB=y
CONFIG_NET_SCH_SFQ=y
CONFIG_NET_SCH_TEQL=y
CONFIG_NET_SCH_TBF=y
CONFIG_NET_SCH_CBS=y
CONFIG_NET_SCH_ETF=y
CONFIG_NET_SCH_TAPRIO=y
CONFIG_NET_SCH_GRED=y
CONFIG_NET_SCH_DSMARK=y
CONFIG_NET_SCH_NETEM=y
CONFIG_NET_SCH_DRR=y
CONFIG_NET_SCH_MQPRIO=y
CONFIG_NET_SCH_SKBPRIO=y
CONFIG_NET_SCH_CHOKE=y
CONFIG_NET_SCH_QFQ=y
CONFIG_NET_SCH_CODEL=y
CONFIG_NET_SCH_FQ_CODEL=y
CONFIG_NET_SCH_CAKE=y
CONFIG_NET_SCH_FQ=y
CONFIG_NET_SCH_HHF=y
CONFIG_NET_SCH_PIE=y
CONFIG_NET_SCH_FQ_PIE=y
CONFIG_NET_SCH_INGRESS=y
CONFIG_NET_SCH_PLUG=y
CONFIG_NET_SCH_ETS=y
CONFIG_NET_SCH_DEFAULT=y
# CONFIG_DEFAULT_FQ is not set
# CONFIG_DEFAULT_CODEL is not set
# CONFIG_DEFAULT_FQ_CODEL is not set
# CONFIG_DEFAULT_FQ_PIE is not set
# CONFIG_DEFAULT_SFQ is not set
CONFIG_DEFAULT_PFIFO_FAST=y
CONFIG_DEFAULT_NET_SCH="pfifo_fast"

#
# Classification
#
CONFIG_NET_CLS=y
CONFIG_NET_CLS_BASIC=y
CONFIG_NET_CLS_TCINDEX=y
CONFIG_NET_CLS_ROUTE4=y
CONFIG_NET_CLS_FW=y
CONFIG_NET_CLS_U32=y
CONFIG_CLS_U32_PERF=y
CONFIG_CLS_U32_MARK=y
CONFIG_NET_CLS_RSVP=y
CONFIG_NET_CLS_RSVP6=y
CONFIG_NET_CLS_FLOW=y
CONFIG_NET_CLS_CGROUP=y
CONFIG_NET_CLS_BPF=y
CONFIG_NET_CLS_FLOWER=y
CONFIG_NET_CLS_MATCHALL=y
CONFIG_NET_EMATCH=y
CONFIG_NET_EMATCH_STACK=32
CONFIG_NET_EMATCH_CMP=y
CONFIG_NET_EMATCH_NBYTE=y
CONFIG_NET_EMATCH_U32=y
CONFIG_NET_EMATCH_META=y
CONFIG_NET_EMATCH_TEXT=y
CONFIG_NET_EMATCH_CANID=y
CONFIG_NET_EMATCH_IPSET=y
CONFIG_NET_EMATCH_IPT=y
CONFIG_NET_CLS_ACT=y
CONFIG_NET_ACT_POLICE=y
CONFIG_NET_ACT_GACT=y
CONFIG_GACT_PROB=y
CONFIG_NET_ACT_MIRRED=y
CONFIG_NET_ACT_SAMPLE=y
CONFIG_NET_ACT_IPT=y
CONFIG_NET_ACT_NAT=y
CONFIG_NET_ACT_PEDIT=y
CONFIG_NET_ACT_SIMP=y
CONFIG_NET_ACT_SKBEDIT=y
CONFIG_NET_ACT_CSUM=y
CONFIG_NET_ACT_MPLS=y
CONFIG_NET_ACT_VLAN=y
CONFIG_NET_ACT_BPF=y
CONFIG_NET_ACT_CONNMARK=y
CONFIG_NET_ACT_CTINFO=y
CONFIG_NET_ACT_SKBMOD=y
CONFIG_NET_ACT_IFE=y
CONFIG_NET_ACT_TUNNEL_KEY=y
CONFIG_NET_ACT_CT=y
CONFIG_NET_ACT_GATE=y
CONFIG_NET_IFE_SKBMARK=y
CONFIG_NET_IFE_SKBPRIO=y
CONFIG_NET_IFE_SKBTCINDEX=y
CONFIG_NET_TC_SKB_EXT=y
CONFIG_NET_SCH_FIFO=y
CONFIG_DCB=y
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=y
CONFIG_BATMAN_ADV_BATMAN_V=y
CONFIG_BATMAN_ADV_BLA=y
CONFIG_BATMAN_ADV_DAT=y
CONFIG_BATMAN_ADV_NC=y
CONFIG_BATMAN_ADV_MCAST=y
CONFIG_BATMAN_ADV_DEBUG=y
CONFIG_BATMAN_ADV_TRACING=y
CONFIG_OPENVSWITCH=y
CONFIG_OPENVSWITCH_GRE=y
CONFIG_OPENVSWITCH_VXLAN=y
CONFIG_OPENVSWITCH_GENEVE=y
CONFIG_VSOCKETS=y
CONFIG_VSOCKETS_DIAG=y
CONFIG_VSOCKETS_LOOPBACK=y
CONFIG_VIRTIO_VSOCKETS=y
CONFIG_VIRTIO_VSOCKETS_COMMON=y
CONFIG_NETLINK_DIAG=y
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=y
CONFIG_MPLS_ROUTING=y
CONFIG_MPLS_IPTUNNEL=y
CONFIG_NET_NSH=y
CONFIG_HSR=y
CONFIG_NET_SWITCHDEV=y
CONFIG_NET_L3_MASTER_DEV=y
CONFIG_QRTR=y
CONFIG_QRTR_SMD=y
CONFIG_QRTR_TUN=y
CONFIG_QRTR_MHI=y
CONFIG_NET_NCSI=y
CONFIG_NCSI_OEM_CMD_GET_MAC=y
CONFIG_NCSI_OEM_CMD_KEEP_PHY=y
CONFIG_PCPU_DEV_REFCNT=y
CONFIG_RPS=y
CONFIG_RFS_ACCEL=y
CONFIG_SOCK_RX_QUEUE_MAPPING=y
CONFIG_XPS=y
CONFIG_CGROUP_NET_PRIO=y
CONFIG_CGROUP_NET_CLASSID=y
CONFIG_NET_RX_BUSY_POLL=y
CONFIG_BQL=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_NET_FLOW_LIMIT=y

#
# Network testing
#
CONFIG_NET_PKTGEN=y
CONFIG_NET_DROP_MONITOR=y
# end of Network testing
# end of Networking options

CONFIG_HAMRADIO=y

#
# Packet Radio protocols
#
CONFIG_AX25=y
CONFIG_AX25_DAMA_SLAVE=y
CONFIG_NETROM=y
CONFIG_ROSE=y

#
# AX.25 network device drivers
#
CONFIG_MKISS=y
CONFIG_6PACK=y
CONFIG_BPQETHER=y
CONFIG_BAYCOM_SER_FDX=y
CONFIG_BAYCOM_SER_HDX=y
CONFIG_BAYCOM_PAR=y
CONFIG_YAM=y
# end of AX.25 network device drivers

CONFIG_CAN=y
CONFIG_CAN_RAW=y
CONFIG_CAN_BCM=y
CONFIG_CAN_GW=y
CONFIG_CAN_J1939=y
CONFIG_CAN_ISOTP=y
CONFIG_BT=y
CONFIG_BT_BREDR=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
CONFIG_BT_BNEP=y
CONFIG_BT_BNEP_MC_FILTER=y
CONFIG_BT_BNEP_PROTO_FILTER=y
CONFIG_BT_CMTP=y
CONFIG_BT_HIDP=y
CONFIG_BT_HS=y
CONFIG_BT_LE=y
CONFIG_BT_6LOWPAN=y
CONFIG_BT_LEDS=y
CONFIG_BT_MSFTEXT=y
CONFIG_BT_AOSPEXT=y
CONFIG_BT_DEBUGFS=y
CONFIG_BT_SELFTEST=y
CONFIG_BT_SELFTEST_ECDH=y
CONFIG_BT_SELFTEST_SMP=y

#
# Bluetooth device drivers
#
CONFIG_BT_INTEL=y
CONFIG_BT_BCM=y
CONFIG_BT_RTL=y
CONFIG_BT_QCA=y
CONFIG_BT_MTK=y
CONFIG_BT_HCIBTUSB=y
CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y
CONFIG_BT_HCIBTUSB_BCM=y
CONFIG_BT_HCIBTUSB_MTK=y
CONFIG_BT_HCIBTUSB_RTL=y
CONFIG_BT_HCIBTSDIO=y
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_SERDEV=y
CONFIG_BT_HCIUART_H4=y
CONFIG_BT_HCIUART_NOKIA=y
CONFIG_BT_HCIUART_BCSP=y
CONFIG_BT_HCIUART_ATH3K=y
CONFIG_BT_HCIUART_LL=y
CONFIG_BT_HCIUART_3WIRE=y
CONFIG_BT_HCIUART_INTEL=y
CONFIG_BT_HCIUART_BCM=y
CONFIG_BT_HCIUART_RTL=y
CONFIG_BT_HCIUART_QCA=y
CONFIG_BT_HCIUART_AG6XX=y
CONFIG_BT_HCIUART_MRVL=y
CONFIG_BT_HCIBCM203X=y
CONFIG_BT_HCIBPA10X=y
CONFIG_BT_HCIBFUSB=y
CONFIG_BT_HCIDTL1=y
CONFIG_BT_HCIBT3C=y
CONFIG_BT_HCIBLUECARD=y
CONFIG_BT_HCIVHCI=y
CONFIG_BT_MRVL=y
CONFIG_BT_MRVL_SDIO=y
CONFIG_BT_ATH3K=y
CONFIG_BT_MTKSDIO=y
CONFIG_BT_MTKUART=y
CONFIG_BT_QCOMSMD=y
CONFIG_BT_HCIRSI=y
CONFIG_BT_VIRTIO=y
# end of Bluetooth device drivers

CONFIG_AF_RXRPC=y
CONFIG_AF_RXRPC_IPV6=y
CONFIG_AF_RXRPC_INJECT_LOSS=y
CONFIG_AF_RXRPC_DEBUG=y
CONFIG_RXKAD=y
CONFIG_AF_KCM=y
CONFIG_STREAM_PARSER=y
CONFIG_MCTP=y
CONFIG_MCTP_TEST=y
CONFIG_MCTP_FLOWS=y
CONFIG_FIB_RULES=y
CONFIG_WIRELESS=y
CONFIG_WIRELESS_EXT=y
CONFIG_WEXT_CORE=y
CONFIG_WEXT_PROC=y
CONFIG_WEXT_SPY=y
CONFIG_WEXT_PRIV=y
CONFIG_CFG80211=y
CONFIG_NL80211_TESTMODE=y
CONFIG_CFG80211_DEVELOPER_WARNINGS=y
CONFIG_CFG80211_CERTIFICATION_ONUS=y
CONFIG_CFG80211_REQUIRE_SIGNED_REGDB=y
CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS=y
CONFIG_CFG80211_EXTRA_REGDB_KEYDIR=""
CONFIG_CFG80211_REG_CELLULAR_HINTS=y
CONFIG_CFG80211_REG_RELAX_NO_IR=y
CONFIG_CFG80211_DEFAULT_PS=y
CONFIG_CFG80211_DEBUGFS=y
CONFIG_CFG80211_CRDA_SUPPORT=y
CONFIG_CFG80211_WEXT=y
CONFIG_CFG80211_WEXT_EXPORT=y
CONFIG_LIB80211=y
CONFIG_LIB80211_CRYPT_WEP=y
CONFIG_LIB80211_CRYPT_CCMP=y
CONFIG_LIB80211_CRYPT_TKIP=y
CONFIG_LIB80211_DEBUG=y
CONFIG_MAC80211=y
CONFIG_MAC80211_HAS_RC=y
CONFIG_MAC80211_RC_MINSTREL=y
CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
CONFIG_MAC80211_RC_DEFAULT="minstrel_ht"
CONFIG_MAC80211_MESH=y
CONFIG_MAC80211_LEDS=y
CONFIG_MAC80211_DEBUGFS=y
CONFIG_MAC80211_MESSAGE_TRACING=y
CONFIG_MAC80211_DEBUG_MENU=y
CONFIG_MAC80211_NOINLINE=y
CONFIG_MAC80211_VERBOSE_DEBUG=y
CONFIG_MAC80211_MLME_DEBUG=y
CONFIG_MAC80211_STA_DEBUG=y
CONFIG_MAC80211_HT_DEBUG=y
CONFIG_MAC80211_OCB_DEBUG=y
CONFIG_MAC80211_IBSS_DEBUG=y
CONFIG_MAC80211_PS_DEBUG=y
CONFIG_MAC80211_MPL_DEBUG=y
CONFIG_MAC80211_MPATH_DEBUG=y
CONFIG_MAC80211_MHWMP_DEBUG=y
CONFIG_MAC80211_MESH_SYNC_DEBUG=y
CONFIG_MAC80211_MESH_CSA_DEBUG=y
CONFIG_MAC80211_MESH_PS_DEBUG=y
CONFIG_MAC80211_TDLS_DEBUG=y
CONFIG_MAC80211_DEBUG_COUNTERS=y
CONFIG_MAC80211_STA_HASH_MAX_SIZE=0
CONFIG_RFKILL=y
CONFIG_RFKILL_LEDS=y
CONFIG_RFKILL_INPUT=y
CONFIG_RFKILL_GPIO=y
CONFIG_NET_9P=y
CONFIG_NET_9P_FD=y
CONFIG_NET_9P_VIRTIO=y
CONFIG_NET_9P_RDMA=y
CONFIG_NET_9P_DEBUG=y
CONFIG_CAIF=y
CONFIG_CAIF_DEBUG=y
CONFIG_CAIF_NETDEV=y
CONFIG_CAIF_USB=y
CONFIG_CEPH_LIB=y
CONFIG_CEPH_LIB_PRETTYDEBUG=y
CONFIG_CEPH_LIB_USE_DNS_RESOLVER=y
CONFIG_NFC=y
CONFIG_NFC_DIGITAL=y
CONFIG_NFC_NCI=y
CONFIG_NFC_NCI_SPI=y
CONFIG_NFC_NCI_UART=y
CONFIG_NFC_HCI=y
CONFIG_NFC_SHDLC=y

#
# Near Field Communication (NFC) devices
#
CONFIG_NFC_TRF7970A=y
CONFIG_NFC_SIM=y
CONFIG_NFC_PORT100=y
CONFIG_NFC_VIRTUAL_NCI=y
CONFIG_NFC_FDP=y
CONFIG_NFC_FDP_I2C=y
CONFIG_NFC_PN544=y
CONFIG_NFC_PN544_I2C=y
CONFIG_NFC_PN533=y
CONFIG_NFC_PN533_USB=y
CONFIG_NFC_PN533_I2C=y
CONFIG_NFC_PN532_UART=y
CONFIG_NFC_MICROREAD=y
CONFIG_NFC_MICROREAD_I2C=y
CONFIG_NFC_MRVL=y
CONFIG_NFC_MRVL_USB=y
CONFIG_NFC_MRVL_UART=y
CONFIG_NFC_MRVL_I2C=y
CONFIG_NFC_MRVL_SPI=y
CONFIG_NFC_ST21NFCA=y
CONFIG_NFC_ST21NFCA_I2C=y
CONFIG_NFC_ST_NCI=y
CONFIG_NFC_ST_NCI_I2C=y
CONFIG_NFC_ST_NCI_SPI=y
CONFIG_NFC_NXP_NCI=y
CONFIG_NFC_NXP_NCI_I2C=y
CONFIG_NFC_S3FWRN5=y
CONFIG_NFC_S3FWRN5_I2C=y
CONFIG_NFC_S3FWRN82_UART=y
CONFIG_NFC_ST95HF=y
# end of Near Field Communication (NFC) devices

CONFIG_PSAMPLE=y
CONFIG_NET_IFE=y
CONFIG_LWTUNNEL=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_DST_CACHE=y
CONFIG_GRO_CELLS=y
CONFIG_SOCK_VALIDATE_XMIT=y
CONFIG_NET_SELFTESTS=y
CONFIG_NET_SOCK_MSG=y
CONFIG_NET_DEVLINK=y
CONFIG_PAGE_POOL=y
CONFIG_PAGE_POOL_STATS=y
CONFIG_FAILOVER=y
CONFIG_ETHTOOL_NETLINK=y
CONFIG_NETDEV_ADDR_LIST_TEST=y

#
# Device Drivers
#
CONFIG_HAVE_PCI=y
CONFIG_PCI=y
CONFIG_PCI_DOMAINS=y
CONFIG_PCI_SYSCALL=y
CONFIG_PCIEPORTBUS=y
CONFIG_HOTPLUG_PCI_PCIE=y
CONFIG_PCIEAER=y
CONFIG_PCIEAER_INJECT=y
CONFIG_PCIE_ECRC=y
CONFIG_PCIEASPM=y
CONFIG_PCIEASPM_DEFAULT=y
# CONFIG_PCIEASPM_POWERSAVE is not set
# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
# CONFIG_PCIEASPM_PERFORMANCE is not set
CONFIG_PCIE_PME=y
CONFIG_PCIE_DPC=y
CONFIG_PCIE_PTM=y
CONFIG_PCI_MSI=y
CONFIG_PCI_MSI_IRQ_DOMAIN=y
CONFIG_PCI_MSI_ARCH_FALLBACKS=y
CONFIG_PCI_QUIRKS=y
CONFIG_PCI_DEBUG=y
CONFIG_PCI_REALLOC_ENABLE_AUTO=y
CONFIG_PCI_STUB=y
CONFIG_PCI_PF_STUB=y
CONFIG_PCI_ATS=y
CONFIG_PCI_DOE=y
CONFIG_PCI_ECAM=y
CONFIG_PCI_BRIDGE_EMUL=y
CONFIG_PCI_IOV=y
CONFIG_PCI_PRI=y
CONFIG_PCI_PASID=y
# CONFIG_PCIE_BUS_TUNE_OFF is not set
CONFIG_PCIE_BUS_DEFAULT=y
# CONFIG_PCIE_BUS_SAFE is not set
# CONFIG_PCIE_BUS_PERFORMANCE is not set
# CONFIG_PCIE_BUS_PEER2PEER is not set
CONFIG_VGA_ARB=y
CONFIG_VGA_ARB_MAX_GPUS=16
CONFIG_HOTPLUG_PCI=y
CONFIG_HOTPLUG_PCI_CPCI=y
CONFIG_HOTPLUG_PCI_SHPC=y

#
# PCI controller drivers
#
CONFIG_PCI_AARDVARK=y
CONFIG_PCIE_XILINX_NWL=y
CONFIG_PCI_FTPCI100=y
CONFIG_PCI_TEGRA=y
CONFIG_PCIE_RCAR_HOST=y
CONFIG_PCIE_RCAR_EP=y
CONFIG_PCI_HOST_COMMON=y
CONFIG_PCI_HOST_GENERIC=y
CONFIG_PCIE_XILINX=y
CONFIG_PCIE_XILINX_CPM=y
CONFIG_PCI_XGENE=y
CONFIG_PCI_XGENE_MSI=y
CONFIG_PCI_V3_SEMI=y
CONFIG_PCI_VERSATILE=y
CONFIG_PCIE_ALTERA=y
CONFIG_PCIE_ALTERA_MSI=y
CONFIG_PCI_HOST_THUNDER_PEM=y
CONFIG_PCI_HOST_THUNDER_ECAM=y
CONFIG_PCIE_ROCKCHIP=y
CONFIG_PCIE_ROCKCHIP_HOST=y
CONFIG_PCIE_ROCKCHIP_EP=y
CONFIG_PCIE_MEDIATEK=y
CONFIG_PCIE_MEDIATEK_GEN3=y
CONFIG_PCIE_BRCMSTB=y
CONFIG_PCI_LOONGSON=y
CONFIG_PCIE_MICROCHIP_HOST=y
CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR=0xfffff000
CONFIG_PCIE_APPLE=y
CONFIG_PCIE_MT7621=y

#
# DesignWare PCI Core Support
#
CONFIG_PCIE_DW=y
CONFIG_PCIE_DW_HOST=y
CONFIG_PCIE_DW_EP=y
CONFIG_PCI_DRA7XX=y
CONFIG_PCI_DRA7XX_HOST=y
CONFIG_PCI_DRA7XX_EP=y
CONFIG_PCIE_DW_PLAT=y
CONFIG_PCIE_DW_PLAT_HOST=y
CONFIG_PCIE_DW_PLAT_EP=y
CONFIG_PCI_EXYNOS=y
CONFIG_PCI_IMX6=y
CONFIG_PCIE_SPEAR13XX=y
CONFIG_PCI_KEYSTONE=y
CONFIG_PCI_KEYSTONE_HOST=y
CONFIG_PCI_KEYSTONE_EP=y
CONFIG_PCI_LAYERSCAPE=y
CONFIG_PCI_LAYERSCAPE_EP=y
CONFIG_PCI_HISI=y
CONFIG_PCIE_QCOM=y
CONFIG_PCIE_QCOM_EP=y
CONFIG_PCIE_ARMADA_8K=y
CONFIG_PCIE_ARTPEC6=y
CONFIG_PCIE_ARTPEC6_HOST=y
CONFIG_PCIE_ARTPEC6_EP=y
CONFIG_PCIE_ROCKCHIP_DW_HOST=y
CONFIG_PCIE_INTEL_GW=y
CONFIG_PCIE_KEEMBAY=y
CONFIG_PCIE_KEEMBAY_HOST=y
CONFIG_PCIE_KEEMBAY_EP=y
CONFIG_PCIE_KIRIN=y
CONFIG_PCIE_HISI_STB=y
CONFIG_PCI_MESON=y
CONFIG_PCIE_TEGRA194=y
CONFIG_PCIE_TEGRA194_HOST=y
CONFIG_PCIE_TEGRA194_EP=y
CONFIG_PCIE_VISCONTI_HOST=y
CONFIG_PCIE_UNIPHIER=y
CONFIG_PCIE_UNIPHIER_EP=y
CONFIG_PCIE_AL=y
CONFIG_PCIE_FU740=y
# end of DesignWare PCI Core Support

#
# Mobiveil PCIe Core Support
#
CONFIG_PCIE_MOBIVEIL=y
CONFIG_PCIE_MOBIVEIL_HOST=y
CONFIG_PCIE_MOBIVEIL_PLAT=y
CONFIG_PCIE_LAYERSCAPE_GEN4=y
# end of Mobiveil PCIe Core Support

#
# Cadence PCIe controllers support
#
CONFIG_PCIE_CADENCE=y
CONFIG_PCIE_CADENCE_HOST=y
CONFIG_PCIE_CADENCE_EP=y
CONFIG_PCIE_CADENCE_PLAT=y
CONFIG_PCIE_CADENCE_PLAT_HOST=y
CONFIG_PCIE_CADENCE_PLAT_EP=y
CONFIG_PCI_J721E=y
CONFIG_PCI_J721E_HOST=y
CONFIG_PCI_J721E_EP=y
# end of Cadence PCIe controllers support
# end of PCI controller drivers

#
# PCI Endpoint
#
CONFIG_PCI_ENDPOINT=y
CONFIG_PCI_ENDPOINT_CONFIGFS=y
CONFIG_PCI_EPF_TEST=y
CONFIG_PCI_EPF_NTB=y
CONFIG_PCI_EPF_VNTB=y
# end of PCI Endpoint

#
# PCI switch controller drivers
#
CONFIG_PCI_SW_SWITCHTEC=y
# end of PCI switch controller drivers

CONFIG_CXL_BUS=y
CONFIG_CXL_PCI=y
CONFIG_CXL_MEM_RAW_COMMANDS=y
CONFIG_CXL_PMEM=y
CONFIG_CXL_MEM=y
CONFIG_CXL_PORT=y
CONFIG_CXL_REGION=y
CONFIG_PCCARD=y
CONFIG_PCMCIA=y
CONFIG_PCMCIA_LOAD_CIS=y
CONFIG_CARDBUS=y

#
# PC-card bridges
#
CONFIG_YENTA=y
CONFIG_YENTA_O2=y
CONFIG_YENTA_RICOH=y
CONFIG_YENTA_TI=y
CONFIG_YENTA_ENE_TUNE=y
CONFIG_YENTA_TOSHIBA=y
CONFIG_PD6729=y
CONFIG_I82092=y
CONFIG_PCCARD_NONSTATIC=y
CONFIG_RAPIDIO=y
CONFIG_RAPIDIO_TSI721=y
CONFIG_RAPIDIO_DISC_TIMEOUT=30
CONFIG_RAPIDIO_ENABLE_RX_TX_PORTS=y
CONFIG_RAPIDIO_DMA_ENGINE=y
CONFIG_RAPIDIO_DEBUG=y
CONFIG_RAPIDIO_ENUM_BASIC=y
CONFIG_RAPIDIO_CHMAN=y
CONFIG_RAPIDIO_MPORT_CDEV=y

#
# RapidIO Switch drivers
#
CONFIG_RAPIDIO_CPS_XX=y
CONFIG_RAPIDIO_CPS_GEN2=y
CONFIG_RAPIDIO_RXS_GEN3=y
# end of RapidIO Switch drivers

#
# Generic Driver Options
#
CONFIG_AUXILIARY_BUS=y
CONFIG_UEVENT_HELPER=y
CONFIG_UEVENT_HELPER_PATH=""
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_DEVTMPFS_SAFE=y
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y

#
# Firmware loader
#
CONFIG_FW_LOADER=y
CONFIG_FW_LOADER_PAGED_BUF=y
CONFIG_FW_LOADER_SYSFS=y
CONFIG_EXTRA_FIRMWARE=""
CONFIG_FW_LOADER_USER_HELPER=y
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
CONFIG_FW_LOADER_COMPRESS=y
CONFIG_FW_LOADER_COMPRESS_XZ=y
CONFIG_FW_LOADER_COMPRESS_ZSTD=y
CONFIG_FW_CACHE=y
CONFIG_FW_UPLOAD=y
# end of Firmware loader

CONFIG_WANT_DEV_COREDUMP=y
CONFIG_ALLOW_DEV_COREDUMP=y
CONFIG_DEV_COREDUMP=y
CONFIG_DEBUG_DRIVER=y
CONFIG_DEBUG_DEVRES=y
CONFIG_DEBUG_TEST_DRIVER_REMOVE=y
CONFIG_PM_QOS_KUNIT_TEST=y
CONFIG_TEST_ASYNC_DRIVER_PROBE=m
CONFIG_DRIVER_PE_KUNIT_TEST=y
CONFIG_SOC_BUS=y
CONFIG_REGMAP=y
CONFIG_REGMAP_AC97=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_SLIMBUS=y
CONFIG_REGMAP_SPI=y
CONFIG_REGMAP_SPMI=y
CONFIG_REGMAP_W1=y
CONFIG_REGMAP_MMIO=y
CONFIG_REGMAP_IRQ=y
CONFIG_REGMAP_SOUNDWIRE=y
CONFIG_REGMAP_SOUNDWIRE_MBQ=y
CONFIG_REGMAP_SCCB=y
CONFIG_REGMAP_I3C=y
CONFIG_REGMAP_SPI_AVMM=y
CONFIG_DMA_SHARED_BUFFER=y
CONFIG_DMA_FENCE_TRACE=y
# end of Generic Driver Options

#
# Bus devices
#
CONFIG_ARM_INTEGRATOR_LM=y
CONFIG_BT1_APB=y
CONFIG_BT1_AXI=y
CONFIG_MOXTET=y
CONFIG_HISILICON_LPC=y
CONFIG_INTEL_IXP4XX_EB=y
CONFIG_QCOM_EBI2=y
CONFIG_MHI_BUS=y
CONFIG_MHI_BUS_DEBUG=y
CONFIG_MHI_BUS_PCI_GENERIC=y
CONFIG_MHI_BUS_EP=y
# end of Bus devices

CONFIG_CONNECTOR=y
CONFIG_PROC_EVENTS=y

#
# Firmware Drivers
#

#
# ARM System Control and Management Interface Protocol
#
CONFIG_ARM_SCMI_PROTOCOL=y
CONFIG_ARM_SCMI_HAVE_TRANSPORT=y
CONFIG_ARM_SCMI_HAVE_SHMEM=y
CONFIG_ARM_SCMI_HAVE_MSG=y
CONFIG_ARM_SCMI_TRANSPORT_MAILBOX=y
CONFIG_ARM_SCMI_TRANSPORT_VIRTIO=y
CONFIG_ARM_SCMI_TRANSPORT_VIRTIO_VERSION1_COMPLIANCE=y
CONFIG_ARM_SCMI_TRANSPORT_VIRTIO_ATOMIC_ENABLE=y
CONFIG_ARM_SCMI_POWER_DOMAIN=y
CONFIG_ARM_SCMI_POWER_CONTROL=y
# end of ARM System Control and Management Interface Protocol

CONFIG_ARM_SCPI_PROTOCOL=y
CONFIG_ARM_SCPI_POWER_DOMAIN=y
CONFIG_FIRMWARE_MEMMAP=y
CONFIG_FW_CFG_SYSFS=y
CONFIG_FW_CFG_SYSFS_CMDLINE=y
CONFIG_MTK_ADSP_IPC=y
CONFIG_QCOM_SCM=y
CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT=y
CONFIG_TURRIS_MOX_RWTM=y
CONFIG_BCM47XX_NVRAM=y
CONFIG_BCM47XX_SPROM=y
CONFIG_TEE_BNXT_FW=y
CONFIG_CS_DSP=y
CONFIG_GOOGLE_FIRMWARE=y
CONFIG_GOOGLE_COREBOOT_TABLE=y
CONFIG_GOOGLE_MEMCONSOLE=y
CONFIG_GOOGLE_MEMCONSOLE_COREBOOT=y
CONFIG_GOOGLE_VPD=y
CONFIG_IMX_DSP=y
CONFIG_IMX_SCU=y
CONFIG_IMX_SCU_PD=y

#
# Tegra firmware driver
#
# end of Tegra firmware driver
# end of Firmware Drivers

CONFIG_GNSS=y
CONFIG_GNSS_SERIAL=y
CONFIG_GNSS_MTK_SERIAL=y
CONFIG_GNSS_SIRF_SERIAL=y
CONFIG_GNSS_UBX_SERIAL=y
CONFIG_GNSS_USB=y
CONFIG_MTD=y
CONFIG_MTD_TESTS=m

#
# Partition parsers
#
CONFIG_MTD_AR7_PARTS=y
CONFIG_MTD_BCM63XX_PARTS=y
CONFIG_MTD_BRCM_U_BOOT=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_OF_PARTS=y
CONFIG_MTD_OF_PARTS_BCM4908=y
CONFIG_MTD_OF_PARTS_LINKSYS_NS=y
CONFIG_MTD_PARSER_IMAGETAG=y
CONFIG_MTD_PARSER_TRX=y
CONFIG_MTD_SHARPSL_PARTS=y
CONFIG_MTD_REDBOOT_PARTS=y
CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y
CONFIG_MTD_REDBOOT_PARTS_READONLY=y
CONFIG_MTD_QCOMSMEM_PARTS=y
# end of Partition parsers

#
# User Modules And Translation Layers
#
CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y

#
# Note that in some cases UBI block is preferred. See MTD_UBI_BLOCK.
#
CONFIG_FTL=y
CONFIG_NFTL=y
CONFIG_NFTL_RW=y
CONFIG_INFTL=y
CONFIG_RFD_FTL=y
CONFIG_SSFDC=y
CONFIG_SM_FTL=y
CONFIG_MTD_OOPS=y
CONFIG_MTD_PSTORE=y
CONFIG_MTD_SWAP=y
CONFIG_MTD_PARTITIONED_MASTER=y

#
# RAM/ROM/Flash chip drivers
#
CONFIG_MTD_CFI=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_GEN_PROBE=y
CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_NOSWAP=y
# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
CONFIG_MTD_CFI_GEOMETRY=y
CONFIG_MTD_MAP_BANK_WIDTH_1=y
CONFIG_MTD_MAP_BANK_WIDTH_2=y
CONFIG_MTD_MAP_BANK_WIDTH_4=y
CONFIG_MTD_MAP_BANK_WIDTH_8=y
CONFIG_MTD_MAP_BANK_WIDTH_16=y
CONFIG_MTD_MAP_BANK_WIDTH_32=y
CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
CONFIG_MTD_CFI_I4=y
CONFIG_MTD_CFI_I8=y
CONFIG_MTD_OTP=y
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_CFI_STAA=y
CONFIG_MTD_CFI_UTIL=y
CONFIG_MTD_RAM=y
CONFIG_MTD_ROM=y
CONFIG_MTD_ABSENT=y
# end of RAM/ROM/Flash chip drivers

#
# Mapping drivers for chip access
#
CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_PHYSMAP_COMPAT=y
CONFIG_MTD_PHYSMAP_START=0x8000000
CONFIG_MTD_PHYSMAP_LEN=0
CONFIG_MTD_PHYSMAP_BANKWIDTH=2
CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_PHYSMAP_BT1_ROM=y
CONFIG_MTD_PHYSMAP_VERSATILE=y
CONFIG_MTD_PHYSMAP_GEMINI=y
CONFIG_MTD_PHYSMAP_GPIO_ADDR=y
CONFIG_MTD_SUN_UFLASH=y
CONFIG_MTD_SC520CDP=y
CONFIG_MTD_NETSC520=y
CONFIG_MTD_TS5500=y
CONFIG_MTD_PCI=y
CONFIG_MTD_PCMCIA=y
CONFIG_MTD_PCMCIA_ANONYMOUS=y
CONFIG_MTD_INTEL_VR_NOR=y
CONFIG_MTD_PLATRAM=y
# end of Mapping drivers for chip access

#
# Self-contained MTD device drivers
#
CONFIG_MTD_PMC551=y
CONFIG_MTD_PMC551_BUGFIX=y
CONFIG_MTD_PMC551_DEBUG=y
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_DATAFLASH_WRITE_VERIFY=y
CONFIG_MTD_DATAFLASH_OTP=y
CONFIG_MTD_MCHP23K256=y
CONFIG_MTD_MCHP48L640=y
CONFIG_MTD_SPEAR_SMI=y
CONFIG_MTD_SST25L=y
CONFIG_MTD_SLRAM=y
CONFIG_MTD_PHRAM=y
CONFIG_MTD_MTDRAM=y
CONFIG_MTDRAM_TOTAL_SIZE=4096
CONFIG_MTDRAM_ERASE_SIZE=128
CONFIG_MTD_BLOCK2MTD=y

#
# Disk-On-Chip Device Drivers
#
CONFIG_MTD_DOCG3=y
CONFIG_BCH_CONST_M=14
CONFIG_BCH_CONST_T=4
# end of Self-contained MTD device drivers

#
# NAND
#
CONFIG_MTD_NAND_CORE=y
CONFIG_MTD_ONENAND=y
CONFIG_MTD_ONENAND_VERIFY_WRITE=y
CONFIG_MTD_ONENAND_GENERIC=y
CONFIG_MTD_ONENAND_SAMSUNG=y
CONFIG_MTD_ONENAND_OTP=y
CONFIG_MTD_ONENAND_2X_PROGRAM=y
CONFIG_MTD_RAW_NAND=y

#
# Raw/parallel NAND flash controllers
#
CONFIG_MTD_NAND_DENALI=y
CONFIG_MTD_NAND_DENALI_PCI=y
CONFIG_MTD_NAND_DENALI_DT=y
CONFIG_MTD_NAND_AMS_DELTA=y
CONFIG_MTD_NAND_SHARPSL=y
CONFIG_MTD_NAND_CAFE=y
CONFIG_MTD_NAND_ATMEL=y
CONFIG_MTD_NAND_MARVELL=y
CONFIG_MTD_NAND_SLC_LPC32XX=y
CONFIG_MTD_NAND_MLC_LPC32XX=y
CONFIG_MTD_NAND_BRCMNAND=y
CONFIG_MTD_NAND_BRCMNAND_BCM63XX=y
CONFIG_MTD_NAND_BRCMNAND_BCMA=y
CONFIG_MTD_NAND_BRCMNAND_BCMBCA=y
CONFIG_MTD_NAND_BRCMNAND_BRCMSTB=y
CONFIG_MTD_NAND_BRCMNAND_IPROC=y
CONFIG_MTD_NAND_BCM47XXNFLASH=y
CONFIG_MTD_NAND_OXNAS=y
CONFIG_MTD_NAND_GPMI_NAND=y
CONFIG_MTD_NAND_FSL_IFC=y
CONFIG_MTD_NAND_VF610_NFC=y
CONFIG_MTD_NAND_MXC=y
CONFIG_MTD_NAND_SH_FLCTL=y
CONFIG_MTD_NAND_DAVINCI=y
CONFIG_MTD_NAND_TXX9NDFMC=y
CONFIG_MTD_NAND_JZ4780=y
CONFIG_MTD_NAND_INGENIC_ECC=y
CONFIG_MTD_NAND_JZ4740_ECC=y
CONFIG_MTD_NAND_JZ4725B_BCH=y
CONFIG_MTD_NAND_JZ4780_BCH=y
CONFIG_MTD_NAND_FSMC=y
CONFIG_MTD_NAND_SUNXI=y
CONFIG_MTD_NAND_HISI504=y
CONFIG_MTD_NAND_QCOM=y
CONFIG_MTD_NAND_MTK=y
CONFIG_MTD_NAND_MXIC=y
CONFIG_MTD_NAND_TEGRA=y
CONFIG_MTD_NAND_STM32_FMC2=y
CONFIG_MTD_NAND_MESON=y
CONFIG_MTD_NAND_GPIO=y
CONFIG_MTD_NAND_PLATFORM=y
CONFIG_MTD_NAND_CADENCE=y
CONFIG_MTD_NAND_ARASAN=y
CONFIG_MTD_NAND_INTEL_LGM=y
CONFIG_MTD_NAND_RENESAS=y

#
# Misc
#
CONFIG_MTD_SM_COMMON=y
CONFIG_MTD_NAND_NANDSIM=y
CONFIG_MTD_NAND_RICOH=y
CONFIG_MTD_NAND_DISKONCHIP=y
CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED=y
CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS=0
CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH=y
CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE=y
CONFIG_MTD_SPI_NAND=y

#
# ECC engine support
#
CONFIG_MTD_NAND_ECC=y
CONFIG_MTD_NAND_ECC_SW_HAMMING=y
CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC=y
CONFIG_MTD_NAND_ECC_SW_BCH=y
CONFIG_MTD_NAND_ECC_MXIC=y
CONFIG_MTD_NAND_ECC_MEDIATEK=y
# end of ECC engine support
# end of NAND

#
# LPDDR & LPDDR2 PCM memory drivers
#
CONFIG_MTD_LPDDR=y
CONFIG_MTD_QINFO_PROBE=y
# end of LPDDR & LPDDR2 PCM memory drivers

CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y
# CONFIG_MTD_SPI_NOR_SWP_DISABLE is not set
CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y
# CONFIG_MTD_SPI_NOR_SWP_KEEP is not set
CONFIG_SPI_HISI_SFC=y
CONFIG_SPI_NXP_SPIFI=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_WL_THRESHOLD=4096
CONFIG_MTD_UBI_BEB_LIMIT=20
CONFIG_MTD_UBI_FASTMAP=y
CONFIG_MTD_UBI_GLUEBI=y
CONFIG_MTD_UBI_BLOCK=y
CONFIG_MTD_HYPERBUS=y
CONFIG_HBMC_AM654=y
CONFIG_DTC=y
CONFIG_OF=y
CONFIG_OF_ALL_DTBS=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_PROMTREE=y
CONFIG_OF_KOBJ=y
CONFIG_OF_DYNAMIC=y
CONFIG_OF_RESERVED_MEM=y
CONFIG_OF_RESOLVE=y
CONFIG_OF_OVERLAY=y
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
CONFIG_PARPORT=y
CONFIG_PARPORT_PC=y
CONFIG_PARPORT_SERIAL=y
CONFIG_PARPORT_PC_FIFO=y
CONFIG_PARPORT_PC_SUPERIO=y
CONFIG_PARPORT_PC_PCMCIA=y
CONFIG_PARPORT_SUNBPP=y
CONFIG_PARPORT_AX88796=y
CONFIG_PARPORT_1284=y
CONFIG_PARPORT_NOT_PC=y
CONFIG_BLK_DEV=y
CONFIG_BLK_DEV_NULL_BLK=y
CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION=y
CONFIG_BLK_DEV_FD=y
CONFIG_BLK_DEV_FD_RAWCMD=y
CONFIG_CDROM=y
CONFIG_PARIDE=y

#
# Parallel IDE high-level drivers
#
CONFIG_PARIDE_PD=y
CONFIG_PARIDE_PCD=y
CONFIG_PARIDE_PF=y
CONFIG_PARIDE_PT=y
CONFIG_PARIDE_PG=y

#
# Parallel IDE protocol modules
#
CONFIG_PARIDE_ATEN=y
CONFIG_PARIDE_BPCK=y
CONFIG_PARIDE_COMM=y
CONFIG_PARIDE_DSTR=y
CONFIG_PARIDE_FIT2=y
CONFIG_PARIDE_FIT3=y
CONFIG_PARIDE_EPAT=y
CONFIG_PARIDE_EPATC8=y
CONFIG_PARIDE_EPIA=y
CONFIG_PARIDE_FRIQ=y
CONFIG_PARIDE_FRPW=y
CONFIG_PARIDE_KBIC=y
CONFIG_PARIDE_KTTI=y
CONFIG_PARIDE_ON20=y
CONFIG_PARIDE_ON26=y
CONFIG_BLK_DEV_PCIESSD_MTIP32XX=y
CONFIG_ZRAM=y
CONFIG_ZRAM_DEF_COMP_LZORLE=y
# CONFIG_ZRAM_DEF_COMP_ZSTD is not set
# CONFIG_ZRAM_DEF_COMP_LZ4 is not set
# CONFIG_ZRAM_DEF_COMP_LZO is not set
# CONFIG_ZRAM_DEF_COMP_LZ4HC is not set
# CONFIG_ZRAM_DEF_COMP_842 is not set
CONFIG_ZRAM_DEF_COMP="lzo-rle"
CONFIG_ZRAM_WRITEBACK=y
CONFIG_ZRAM_MEMORY_TRACKING=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
CONFIG_BLK_DEV_DRBD=y
CONFIG_DRBD_FAULT_INJECTION=y
CONFIG_BLK_DEV_NBD=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
CONFIG_BLK_DEV_RAM_SIZE=4096
CONFIG_CDROM_PKTCDVD=y
CONFIG_CDROM_PKTCDVD_BUFFERS=8
CONFIG_CDROM_PKTCDVD_WCACHE=y
CONFIG_ATA_OVER_ETH=y
CONFIG_SUNVDC=y
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_RBD=y
CONFIG_BLK_DEV_UBLK=y
CONFIG_BLK_DEV_RNBD=y
CONFIG_BLK_DEV_RNBD_CLIENT=y
CONFIG_BLK_DEV_RNBD_SERVER=y

#
# NVME Support
#
CONFIG_NVME_COMMON=y
CONFIG_NVME_CORE=y
CONFIG_BLK_DEV_NVME=y
CONFIG_NVME_MULTIPATH=y
CONFIG_NVME_VERBOSE_ERRORS=y
CONFIG_NVME_HWMON=y
CONFIG_NVME_FABRICS=y
CONFIG_NVME_RDMA=y
CONFIG_NVME_FC=y
CONFIG_NVME_TCP=y
CONFIG_NVME_AUTH=y
CONFIG_NVME_APPLE=y
CONFIG_NVME_TARGET=y
CONFIG_NVME_TARGET_PASSTHRU=y
CONFIG_NVME_TARGET_LOOP=y
CONFIG_NVME_TARGET_RDMA=y
CONFIG_NVME_TARGET_FC=y
CONFIG_NVME_TARGET_FCLOOP=y
CONFIG_NVME_TARGET_TCP=y
CONFIG_NVME_TARGET_AUTH=y
# end of NVME Support

#
# Misc devices
#
CONFIG_SENSORS_LIS3LV02D=y
CONFIG_AD525X_DPOT=y
CONFIG_AD525X_DPOT_I2C=y
CONFIG_AD525X_DPOT_SPI=y
CONFIG_DUMMY_IRQ=y
CONFIG_PHANTOM=y
CONFIG_TIFM_CORE=y
CONFIG_TIFM_7XX1=y
CONFIG_ICS932S401=y
CONFIG_ATMEL_SSC=y
CONFIG_ENCLOSURE_SERVICES=y
CONFIG_GEHC_ACHC=y
CONFIG_HI6421V600_IRQ=y
CONFIG_HP_ILO=y
CONFIG_QCOM_COINCELL=y
CONFIG_QCOM_FASTRPC=y
CONFIG_APDS9802ALS=y
CONFIG_ISL29003=y
CONFIG_ISL29020=y
CONFIG_SENSORS_TSL2550=y
CONFIG_SENSORS_BH1770=y
CONFIG_SENSORS_APDS990X=y
CONFIG_HMC6352=y
CONFIG_DS1682=y
CONFIG_PCH_PHUB=y
CONFIG_LATTICE_ECP3_CONFIG=y
CONFIG_SRAM=y
CONFIG_DW_XDATA_PCIE=y
CONFIG_PCI_ENDPOINT_TEST=y
CONFIG_XILINX_SDFEC=y
CONFIG_MISC_RTSX=y
CONFIG_HISI_HIKEY_USB=y
CONFIG_OPEN_DICE=y
CONFIG_VCPU_STALL_DETECTOR=y
CONFIG_C2PORT=y

#
# EEPROM support
#
CONFIG_EEPROM_AT24=y
CONFIG_EEPROM_AT25=y
CONFIG_EEPROM_LEGACY=y
CONFIG_EEPROM_MAX6875=y
CONFIG_EEPROM_93CX6=y
CONFIG_EEPROM_93XX46=y
CONFIG_EEPROM_IDT_89HPESX=y
CONFIG_EEPROM_EE1004=y
# end of EEPROM support

CONFIG_CB710_CORE=y
CONFIG_CB710_DEBUG=y
CONFIG_CB710_DEBUG_ASSUMPTIONS=y

#
# Texas Instruments shared transport line discipline
#
CONFIG_TI_ST=y
# end of Texas Instruments shared transport line discipline

CONFIG_SENSORS_LIS3_SPI=y
CONFIG_SENSORS_LIS3_I2C=y
CONFIG_ALTERA_STAPL=y
CONFIG_GENWQE=y
CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY=0
CONFIG_ECHO=y
CONFIG_BCM_VK=y
CONFIG_BCM_VK_TTY=y
CONFIG_MISC_ALCOR_PCI=y
CONFIG_MISC_RTSX_PCI=y
CONFIG_MISC_RTSX_USB=y
CONFIG_HABANA_AI=y
CONFIG_UACCE=y
CONFIG_PVPANIC=y
CONFIG_PVPANIC_MMIO=y
CONFIG_PVPANIC_PCI=y
CONFIG_GP_PCI1XXXX=y
# end of Misc devices

#
# SCSI device support
#
CONFIG_SCSI_MOD=y
CONFIG_RAID_ATTRS=y
CONFIG_SCSI_COMMON=y
CONFIG_SCSI=y
CONFIG_SCSI_DMA=y
CONFIG_SCSI_NETLINK=y
CONFIG_SCSI_PROC_FS=y

#
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
CONFIG_BLK_DEV_SR=y
CONFIG_CHR_DEV_SG=y
CONFIG_BLK_DEV_BSG=y
CONFIG_CHR_DEV_SCH=y
CONFIG_SCSI_ENCLOSURE=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y

#
# SCSI Transports
#
CONFIG_SCSI_SPI_ATTRS=y
CONFIG_SCSI_FC_ATTRS=y
CONFIG_SCSI_ISCSI_ATTRS=y
CONFIG_SCSI_SAS_ATTRS=y
CONFIG_SCSI_SAS_LIBSAS=y
CONFIG_SCSI_SAS_ATA=y
CONFIG_SCSI_SAS_HOST_SMP=y
CONFIG_SCSI_SRP_ATTRS=y
# end of SCSI Transports

CONFIG_SCSI_LOWLEVEL=y
CONFIG_ISCSI_TCP=y
CONFIG_ISCSI_BOOT_SYSFS=y
CONFIG_SCSI_CXGB3_ISCSI=y
CONFIG_SCSI_CXGB4_ISCSI=y
CONFIG_SCSI_BNX2_ISCSI=y
CONFIG_SCSI_BNX2X_FCOE=y
CONFIG_BE2ISCSI=y
CONFIG_BLK_DEV_3W_XXXX_RAID=y
CONFIG_SCSI_HPSA=y
CONFIG_SCSI_3W_9XXX=y
CONFIG_SCSI_3W_SAS=y
CONFIG_SCSI_ACARD=y
CONFIG_SCSI_AACRAID=y
CONFIG_SCSI_AIC7XXX=y
CONFIG_AIC7XXX_CMDS_PER_DEVICE=32
CONFIG_AIC7XXX_RESET_DELAY_MS=5000
CONFIG_AIC7XXX_DEBUG_ENABLE=y
CONFIG_AIC7XXX_DEBUG_MASK=0
CONFIG_AIC7XXX_REG_PRETTY_PRINT=y
CONFIG_SCSI_AIC79XX=y
CONFIG_AIC79XX_CMDS_PER_DEVICE=32
CONFIG_AIC79XX_RESET_DELAY_MS=5000
CONFIG_AIC79XX_DEBUG_ENABLE=y
CONFIG_AIC79XX_DEBUG_MASK=0
CONFIG_AIC79XX_REG_PRETTY_PRINT=y
CONFIG_SCSI_AIC94XX=y
CONFIG_AIC94XX_DEBUG=y
CONFIG_SCSI_HISI_SAS=y
CONFIG_SCSI_HISI_SAS_DEBUGFS_DEFAULT_ENABLE=y
CONFIG_SCSI_MVSAS=y
CONFIG_SCSI_MVSAS_DEBUG=y
CONFIG_SCSI_MVSAS_TASKLET=y
CONFIG_SCSI_MVUMI=y
CONFIG_SCSI_ADVANSYS=y
CONFIG_SCSI_ARCMSR=y
CONFIG_SCSI_ESAS2R=y
CONFIG_MEGARAID_NEWGEN=y
CONFIG_MEGARAID_MM=y
CONFIG_MEGARAID_MAILBOX=y
CONFIG_MEGARAID_LEGACY=y
CONFIG_MEGARAID_SAS=y
CONFIG_SCSI_MPT3SAS=y
CONFIG_SCSI_MPT2SAS_MAX_SGE=128
CONFIG_SCSI_MPT3SAS_MAX_SGE=128
CONFIG_SCSI_MPT2SAS=y
CONFIG_SCSI_MPI3MR=y
CONFIG_SCSI_SMARTPQI=y
CONFIG_SCSI_HPTIOP=y
CONFIG_SCSI_BUSLOGIC=y
CONFIG_SCSI_FLASHPOINT=y
CONFIG_SCSI_MYRB=y
CONFIG_SCSI_MYRS=y
CONFIG_LIBFC=y
CONFIG_LIBFCOE=y
CONFIG_FCOE=y
CONFIG_SCSI_SNIC=y
CONFIG_SCSI_SNIC_DEBUG_FS=y
CONFIG_SCSI_DMX3191D=y
CONFIG_SCSI_FDOMAIN=y
CONFIG_SCSI_FDOMAIN_PCI=y
CONFIG_SCSI_IPS=y
CONFIG_SCSI_INITIO=y
CONFIG_SCSI_INIA100=y
CONFIG_SCSI_PPA=y
CONFIG_SCSI_IMM=y
CONFIG_SCSI_IZIP_EPP16=y
CONFIG_SCSI_IZIP_SLOW_CTR=y
CONFIG_SCSI_STEX=y
CONFIG_SCSI_SYM53C8XX_2=y
CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
CONFIG_SCSI_SYM53C8XX_MMIO=y
CONFIG_SCSI_IPR=y
CONFIG_SCSI_IPR_TRACE=y
CONFIG_SCSI_IPR_DUMP=y
CONFIG_SCSI_QLOGIC_1280=y
CONFIG_SCSI_QLOGICPTI=y
CONFIG_SCSI_QLA_FC=y
CONFIG_TCM_QLA2XXX=y
CONFIG_TCM_QLA2XXX_DEBUG=y
CONFIG_SCSI_QLA_ISCSI=y
CONFIG_QEDI=y
CONFIG_QEDF=y
CONFIG_SCSI_LPFC=y
CONFIG_SCSI_LPFC_DEBUG_FS=y
CONFIG_SCSI_EFCT=y
CONFIG_SCSI_DC395x=y
CONFIG_SCSI_AM53C974=y
CONFIG_SCSI_WD719X=y
CONFIG_SCSI_DEBUG=y
CONFIG_SCSI_SUNESP=y
CONFIG_SCSI_PMCRAID=y
CONFIG_SCSI_PM8001=y
CONFIG_SCSI_BFA_FC=y
CONFIG_SCSI_VIRTIO=y
CONFIG_SCSI_CHELSIO_FCOE=y
CONFIG_SCSI_LOWLEVEL_PCMCIA=y
CONFIG_PCMCIA_AHA152X=m
CONFIG_PCMCIA_FDOMAIN=m
CONFIG_PCMCIA_NINJA_SCSI=m
CONFIG_PCMCIA_QLOGIC=m
CONFIG_PCMCIA_SYM53C500=m
CONFIG_SCSI_DH=y
CONFIG_SCSI_DH_RDAC=y
CONFIG_SCSI_DH_HP_SW=y
CONFIG_SCSI_DH_EMC=y
CONFIG_SCSI_DH_ALUA=y
# end of SCSI device support

CONFIG_ATA=y
CONFIG_SATA_HOST=y
CONFIG_PATA_TIMINGS=y
CONFIG_ATA_VERBOSE_ERROR=y
CONFIG_ATA_FORCE=y
CONFIG_SATA_PMP=y

#
# Controllers with non-SFF native interface
#
CONFIG_SATA_AHCI=y
CONFIG_SATA_MOBILE_LPM_POLICY=0
CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_AHCI_BRCM=y
CONFIG_AHCI_DA850=y
CONFIG_AHCI_DM816=y
CONFIG_AHCI_DWC=y
CONFIG_AHCI_ST=y
CONFIG_AHCI_IMX=y
CONFIG_AHCI_CEVA=y
CONFIG_AHCI_MTK=y
CONFIG_AHCI_MVEBU=y
CONFIG_AHCI_SUNXI=y
CONFIG_AHCI_TEGRA=y
CONFIG_AHCI_XGENE=y
CONFIG_AHCI_QORIQ=y
CONFIG_SATA_FSL=y
CONFIG_SATA_GEMINI=y
CONFIG_SATA_AHCI_SEATTLE=y
CONFIG_SATA_INIC162X=y
CONFIG_SATA_ACARD_AHCI=y
CONFIG_SATA_SIL24=y
CONFIG_ATA_SFF=y

#
# SFF controllers with custom DMA interface
#
CONFIG_PDC_ADMA=y
CONFIG_SATA_QSTOR=y
CONFIG_SATA_SX4=y
CONFIG_ATA_BMDMA=y

#
# SATA SFF controllers with BMDMA
#
CONFIG_ATA_PIIX=y
CONFIG_SATA_DWC=y
CONFIG_SATA_DWC_OLD_DMA=y
CONFIG_SATA_HIGHBANK=y
CONFIG_SATA_MV=y
CONFIG_SATA_NV=y
CONFIG_SATA_PROMISE=y
CONFIG_SATA_RCAR=y
CONFIG_SATA_SIL=y
CONFIG_SATA_SIS=y
CONFIG_SATA_SVW=y
CONFIG_SATA_ULI=y
CONFIG_SATA_VIA=y
CONFIG_SATA_VITESSE=y

#
# PATA SFF controllers with BMDMA
#
CONFIG_PATA_ALI=y
CONFIG_PATA_AMD=y
CONFIG_PATA_ARASAN_CF=y
CONFIG_PATA_ARTOP=y
CONFIG_PATA_ATIIXP=y
CONFIG_PATA_ATP867X=y
CONFIG_PATA_BK3710=y
CONFIG_PATA_CMD64X=y
CONFIG_PATA_CS5520=y
CONFIG_PATA_CS5530=y
CONFIG_PATA_CS5536=y
CONFIG_PATA_CYPRESS=y
CONFIG_PATA_EFAR=y
CONFIG_PATA_FTIDE010=y
CONFIG_PATA_HPT366=y
CONFIG_PATA_HPT37X=y
CONFIG_PATA_HPT3X2N=y
CONFIG_PATA_HPT3X3=y
CONFIG_PATA_HPT3X3_DMA=y
CONFIG_PATA_IMX=y
CONFIG_PATA_IT8213=y
CONFIG_PATA_IT821X=y
CONFIG_PATA_JMICRON=y
CONFIG_PATA_MARVELL=y
CONFIG_PATA_NETCELL=y
CONFIG_PATA_NINJA32=y
CONFIG_PATA_NS87415=y
CONFIG_PATA_OLDPIIX=y
CONFIG_PATA_OPTIDMA=y
CONFIG_PATA_PDC2027X=y
CONFIG_PATA_PDC_OLD=y
CONFIG_PATA_RADISYS=y
CONFIG_PATA_RDC=y
CONFIG_PATA_SC1200=y
CONFIG_PATA_SCH=y
CONFIG_PATA_SERVERWORKS=y
CONFIG_PATA_SIL680=y
CONFIG_PATA_SIS=y
CONFIG_PATA_TOSHIBA=y
CONFIG_PATA_TRIFLEX=y
CONFIG_PATA_VIA=y
CONFIG_PATA_PXA=y
CONFIG_PATA_WINBOND=y

#
# PIO-only SFF controllers
#
CONFIG_PATA_CMD640_PCI=y
CONFIG_PATA_IXP4XX_CF=y
CONFIG_PATA_MPIIX=y
CONFIG_PATA_NS87410=y
CONFIG_PATA_OPTI=y
CONFIG_PATA_PCMCIA=y
CONFIG_PATA_PLATFORM=y
CONFIG_PATA_OF_PLATFORM=y
CONFIG_PATA_RZ1000=y
CONFIG_PATA_SAMSUNG_CF=y

#
# Generic fallback / legacy drivers
#
CONFIG_ATA_GENERIC=y
CONFIG_PATA_LEGACY=y
CONFIG_MD=y
CONFIG_BLK_DEV_MD=y
CONFIG_MD_AUTODETECT=y
CONFIG_MD_LINEAR=y
CONFIG_MD_RAID0=y
CONFIG_MD_RAID1=y
CONFIG_MD_RAID10=y
CONFIG_MD_RAID456=y
CONFIG_MD_MULTIPATH=y
CONFIG_MD_FAULTY=y
CONFIG_MD_CLUSTER=y
CONFIG_BCACHE=y
CONFIG_BCACHE_DEBUG=y
CONFIG_BCACHE_CLOSURES_DEBUG=y
CONFIG_BCACHE_ASYNC_REGISTRATION=y
CONFIG_BLK_DEV_DM_BUILTIN=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_DEBUG=y
CONFIG_DM_BUFIO=y
CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING=y
CONFIG_DM_DEBUG_BLOCK_STACK_TRACING=y
CONFIG_DM_BIO_PRISON=y
CONFIG_DM_PERSISTENT_DATA=y
CONFIG_DM_UNSTRIPED=y
CONFIG_DM_CRYPT=y
CONFIG_DM_SNAPSHOT=y
CONFIG_DM_THIN_PROVISIONING=y
CONFIG_DM_CACHE=y
CONFIG_DM_CACHE_SMQ=y
CONFIG_DM_WRITECACHE=y
CONFIG_DM_EBS=y
CONFIG_DM_ERA=y
CONFIG_DM_CLONE=y
CONFIG_DM_MIRROR=y
CONFIG_DM_LOG_USERSPACE=y
CONFIG_DM_RAID=y
CONFIG_DM_ZERO=y
CONFIG_DM_MULTIPATH=y
CONFIG_DM_MULTIPATH_QL=y
CONFIG_DM_MULTIPATH_ST=y
CONFIG_DM_MULTIPATH_HST=y
CONFIG_DM_MULTIPATH_IOA=y
CONFIG_DM_DELAY=y
CONFIG_DM_DUST=y
CONFIG_DM_INIT=y
CONFIG_DM_UEVENT=y
CONFIG_DM_FLAKEY=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING=y
CONFIG_DM_VERITY_FEC=y
CONFIG_DM_SWITCH=y
CONFIG_DM_LOG_WRITES=y
CONFIG_DM_INTEGRITY=y
CONFIG_DM_ZONED=y
CONFIG_DM_AUDIT=y
CONFIG_TARGET_CORE=y
CONFIG_TCM_IBLOCK=y
CONFIG_TCM_FILEIO=y
CONFIG_TCM_PSCSI=y
CONFIG_TCM_USER2=y
CONFIG_LOOPBACK_TARGET=y
CONFIG_TCM_FC=y
CONFIG_ISCSI_TARGET=y
CONFIG_ISCSI_TARGET_CXGB4=y
CONFIG_SBP_TARGET=y
CONFIG_FUSION=y
CONFIG_FUSION_SPI=y
CONFIG_FUSION_FC=y
CONFIG_FUSION_SAS=y
CONFIG_FUSION_MAX_SGE=128
CONFIG_FUSION_CTL=y
CONFIG_FUSION_LAN=y
CONFIG_FUSION_LOGGING=y

#
# IEEE 1394 (FireWire) support
#
CONFIG_FIREWIRE=y
CONFIG_FIREWIRE_OHCI=y
CONFIG_FIREWIRE_SBP2=y
CONFIG_FIREWIRE_NET=y
CONFIG_FIREWIRE_NOSY=y
# end of IEEE 1394 (FireWire) support

CONFIG_NETDEVICES=y
CONFIG_MII=y
CONFIG_NET_CORE=y
CONFIG_BONDING=y
CONFIG_DUMMY=y
CONFIG_WIREGUARD=y
CONFIG_WIREGUARD_DEBUG=y
CONFIG_EQUALIZER=y
CONFIG_NET_FC=y
CONFIG_IFB=y
CONFIG_NET_TEAM=y
CONFIG_NET_TEAM_MODE_BROADCAST=y
CONFIG_NET_TEAM_MODE_ROUNDROBIN=y
CONFIG_NET_TEAM_MODE_RANDOM=y
CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=y
CONFIG_NET_TEAM_MODE_LOADBALANCE=y
CONFIG_MACVLAN=y
CONFIG_MACVTAP=y
CONFIG_IPVLAN_L3S=y
CONFIG_IPVLAN=y
CONFIG_IPVTAP=y
CONFIG_VXLAN=y
CONFIG_GENEVE=y
CONFIG_BAREUDP=y
CONFIG_GTP=y
CONFIG_AMT=y
CONFIG_MACSEC=y
CONFIG_NETCONSOLE=y
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETPOLL=y
CONFIG_NET_POLL_CONTROLLER=y
CONFIG_NTB_NETDEV=y
CONFIG_RIONET=y
CONFIG_RIONET_TX_SIZE=128
CONFIG_RIONET_RX_SIZE=128
CONFIG_TUN=y
CONFIG_TAP=y
CONFIG_TUN_VNET_CROSS_LE=y
CONFIG_VETH=y
CONFIG_VIRTIO_NET=y
CONFIG_NLMON=y
CONFIG_NET_VRF=y
CONFIG_VSOCKMON=y
CONFIG_MHI_NET=y
CONFIG_SUNGEM_PHY=y
CONFIG_ARCNET=y
CONFIG_ARCNET_1201=y
CONFIG_ARCNET_1051=y
CONFIG_ARCNET_RAW=y
CONFIG_ARCNET_CAP=y
CONFIG_ARCNET_COM90xx=y
CONFIG_ARCNET_COM90xxIO=y
CONFIG_ARCNET_RIM_I=y
CONFIG_ARCNET_COM20020=y
CONFIG_ARCNET_COM20020_PCI=y
CONFIG_ARCNET_COM20020_CS=y
CONFIG_ATM_DRIVERS=y
CONFIG_ATM_DUMMY=y
CONFIG_ATM_TCP=y
CONFIG_ATM_LANAI=y
CONFIG_ATM_ENI=y
CONFIG_ATM_ENI_DEBUG=y
CONFIG_ATM_ENI_TUNE_BURST=y
CONFIG_ATM_ENI_BURST_TX_16W=y
CONFIG_ATM_ENI_BURST_TX_8W=y
CONFIG_ATM_ENI_BURST_TX_4W=y
CONFIG_ATM_ENI_BURST_TX_2W=y
CONFIG_ATM_ENI_BURST_RX_16W=y
CONFIG_ATM_ENI_BURST_RX_8W=y
CONFIG_ATM_ENI_BURST_RX_4W=y
CONFIG_ATM_ENI_BURST_RX_2W=y
CONFIG_ATM_NICSTAR=y
CONFIG_ATM_NICSTAR_USE_SUNI=y
CONFIG_ATM_NICSTAR_USE_IDT77105=y
CONFIG_ATM_IDT77252=y
CONFIG_ATM_IDT77252_DEBUG=y
CONFIG_ATM_IDT77252_RCV_ALL=y
CONFIG_ATM_IDT77252_USE_SUNI=y
CONFIG_ATM_IA=y
CONFIG_ATM_IA_DEBUG=y
CONFIG_ATM_FORE200E=y
CONFIG_ATM_FORE200E_USE_TASKLET=y
CONFIG_ATM_FORE200E_TX_RETRY=16
CONFIG_ATM_FORE200E_DEBUG=0
CONFIG_ATM_HE=y
CONFIG_ATM_HE_USE_SUNI=y
CONFIG_ATM_SOLOS=y
CONFIG_CAIF_DRIVERS=y
CONFIG_CAIF_TTY=y
CONFIG_CAIF_VIRTIO=y

#
# Distributed Switch Architecture drivers
#
CONFIG_B53=y
CONFIG_B53_SPI_DRIVER=y
CONFIG_B53_MDIO_DRIVER=y
CONFIG_B53_MMAP_DRIVER=y
CONFIG_B53_SRAB_DRIVER=y
CONFIG_B53_SERDES=y
CONFIG_NET_DSA_BCM_SF2=y
CONFIG_NET_DSA_LOOP=y
CONFIG_NET_DSA_HIRSCHMANN_HELLCREEK=y
CONFIG_NET_DSA_LANTIQ_GSWIP=y
CONFIG_NET_DSA_MT7530=y
CONFIG_NET_DSA_MV88E6060=y
CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON=y
CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C=y
CONFIG_NET_DSA_MICROCHIP_KSZ_SPI=y
CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI=y
CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_NET_DSA_MV88E6XXX_PTP=y
CONFIG_NET_DSA_MSCC_FELIX=y
CONFIG_NET_DSA_MSCC_SEVILLE=y
CONFIG_NET_DSA_AR9331=y
CONFIG_NET_DSA_QCA8K=y
CONFIG_NET_DSA_SJA1105=y
CONFIG_NET_DSA_SJA1105_PTP=y
CONFIG_NET_DSA_SJA1105_TAS=y
CONFIG_NET_DSA_SJA1105_VL=y
CONFIG_NET_DSA_XRS700X=y
CONFIG_NET_DSA_XRS700X_I2C=y
CONFIG_NET_DSA_XRS700X_MDIO=y
CONFIG_NET_DSA_REALTEK=y
CONFIG_NET_DSA_REALTEK_MDIO=y
CONFIG_NET_DSA_REALTEK_SMI=y
CONFIG_NET_DSA_REALTEK_RTL8365MB=y
CONFIG_NET_DSA_REALTEK_RTL8366RB=y
CONFIG_NET_DSA_SMSC_LAN9303=y
CONFIG_NET_DSA_SMSC_LAN9303_I2C=y
CONFIG_NET_DSA_SMSC_LAN9303_MDIO=y
CONFIG_NET_DSA_VITESSE_VSC73XX=y
CONFIG_NET_DSA_VITESSE_VSC73XX_SPI=y
CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM=y
# end of Distributed Switch Architecture drivers

CONFIG_ETHERNET=y
CONFIG_MDIO=y
CONFIG_NET_VENDOR_3COM=y
CONFIG_PCMCIA_3C574=y
CONFIG_PCMCIA_3C589=y
CONFIG_VORTEX=y
CONFIG_TYPHOON=y
CONFIG_NET_VENDOR_ACTIONS=y
CONFIG_OWL_EMAC=y
CONFIG_NET_VENDOR_ADAPTEC=y
CONFIG_ADAPTEC_STARFIRE=y
CONFIG_GRETH=y
CONFIG_NET_VENDOR_AGERE=y
CONFIG_ET131X=y
CONFIG_NET_VENDOR_ALACRITECH=y
CONFIG_SLICOSS=y
CONFIG_NET_VENDOR_ALTEON=y
CONFIG_ACENIC=y
CONFIG_ACENIC_OMIT_TIGON_I=y
CONFIG_ALTERA_TSE=y
CONFIG_NET_VENDOR_AMAZON=y
CONFIG_NET_VENDOR_AMD=y
CONFIG_AMD8111_ETH=y
CONFIG_PCNET32=y
CONFIG_PCMCIA_NMCLAN=y
CONFIG_SUNLANCE=y
CONFIG_AMD_XGBE=y
CONFIG_AMD_XGBE_DCB=y
CONFIG_NET_XGENE=y
CONFIG_NET_XGENE_V2=y
CONFIG_NET_VENDOR_AQUANTIA=y
CONFIG_AQTION=y
CONFIG_NET_VENDOR_ARC=y
CONFIG_NET_VENDOR_ASIX=y
CONFIG_SPI_AX88796C=y
CONFIG_SPI_AX88796C_COMPRESSION=y
CONFIG_NET_VENDOR_ATHEROS=y
CONFIG_ATL2=y
CONFIG_ATL1=y
CONFIG_ATL1E=y
CONFIG_ATL1C=y
CONFIG_ALX=y
CONFIG_CX_ECAT=y
CONFIG_NET_VENDOR_BROADCOM=y
CONFIG_B44=y
CONFIG_B44_PCI_AUTOSELECT=y
CONFIG_B44_PCICORE_AUTOSELECT=y
CONFIG_B44_PCI=y
CONFIG_BCM4908_ENET=y
CONFIG_BCMGENET=y
CONFIG_BNX2=y
CONFIG_CNIC=y
CONFIG_TIGON3=y
CONFIG_TIGON3_HWMON=y
CONFIG_BNX2X=y
CONFIG_BNX2X_SRIOV=y
CONFIG_BGMAC=y
CONFIG_BGMAC_BCMA=y
CONFIG_BGMAC_PLATFORM=y
CONFIG_SYSTEMPORT=y
CONFIG_BNXT=y
CONFIG_BNXT_SRIOV=y
CONFIG_BNXT_FLOWER_OFFLOAD=y
CONFIG_BNXT_DCB=y
CONFIG_BNXT_HWMON=y
CONFIG_NET_VENDOR_CADENCE=y
CONFIG_MACB=y
CONFIG_MACB_USE_HWSTAMP=y
CONFIG_MACB_PCI=y
CONFIG_NET_CALXEDA_XGMAC=y
CONFIG_NET_VENDOR_CAVIUM=y
CONFIG_THUNDER_NIC_PF=y
CONFIG_THUNDER_NIC_VF=y
CONFIG_THUNDER_NIC_BGX=y
CONFIG_THUNDER_NIC_RGX=y
CONFIG_CAVIUM_PTP=y
CONFIG_LIQUIDIO=y
CONFIG_LIQUIDIO_VF=y
CONFIG_NET_VENDOR_CHELSIO=y
CONFIG_CHELSIO_T1=y
CONFIG_CHELSIO_T1_1G=y
CONFIG_CHELSIO_T3=y
CONFIG_CHELSIO_T4=y
CONFIG_CHELSIO_T4_DCB=y
CONFIG_CHELSIO_T4_FCOE=y
CONFIG_CHELSIO_T4VF=y
CONFIG_CHELSIO_LIB=y
CONFIG_CHELSIO_INLINE_CRYPTO=y
CONFIG_CRYPTO_DEV_CHELSIO_TLS=y
CONFIG_CHELSIO_IPSEC_INLINE=y
CONFIG_CHELSIO_TLS_DEVICE=y
CONFIG_NET_VENDOR_CIRRUS=y
CONFIG_CS89x0=y
CONFIG_CS89x0_PLATFORM=y
CONFIG_EP93XX_ETH=y
CONFIG_NET_VENDOR_CISCO=y
CONFIG_ENIC=y
CONFIG_NET_VENDOR_CORTINA=y
CONFIG_GEMINI_ETHERNET=y
CONFIG_NET_VENDOR_DAVICOM=y
CONFIG_DM9000=y
CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL=y
CONFIG_DM9051=y
CONFIG_DNET=y
CONFIG_NET_VENDOR_DEC=y
CONFIG_NET_TULIP=y
CONFIG_DE2104X=y
CONFIG_DE2104X_DSL=0
CONFIG_TULIP=y
CONFIG_TULIP_MWI=y
CONFIG_TULIP_MMIO=y
CONFIG_TULIP_NAPI=y
CONFIG_TULIP_NAPI_HW_MITIGATION=y
CONFIG_TULIP_DM910X=y
CONFIG_WINBOND_840=y
CONFIG_DM9102=y
CONFIG_ULI526X=y
CONFIG_PCMCIA_XIRCOM=y
CONFIG_NET_VENDOR_DLINK=y
CONFIG_DL2K=y
CONFIG_SUNDANCE=y
CONFIG_SUNDANCE_MMIO=y
CONFIG_NET_VENDOR_EMULEX=y
CONFIG_BE2NET=y
CONFIG_BE2NET_HWMON=y
CONFIG_BE2NET_BE2=y
CONFIG_BE2NET_BE3=y
CONFIG_BE2NET_LANCER=y
CONFIG_BE2NET_SKYHAWK=y
CONFIG_NET_VENDOR_ENGLEDER=y
CONFIG_TSNEP=y
CONFIG_TSNEP_SELFTESTS=y
CONFIG_NET_VENDOR_EZCHIP=y
CONFIG_NET_VENDOR_FARADAY=y
CONFIG_NET_VENDOR_FREESCALE=y
CONFIG_FEC=y
CONFIG_FSL_FMAN=y
CONFIG_FSL_PQ_MDIO=y
CONFIG_FSL_XGMAC_MDIO=y
CONFIG_GIANFAR=y
CONFIG_FSL_DPAA2_SWITCH=y
CONFIG_FSL_ENETC=y
CONFIG_FSL_ENETC_VF=y
CONFIG_FSL_ENETC_IERB=y
CONFIG_FSL_ENETC_MDIO=y
CONFIG_FSL_ENETC_PTP_CLOCK=y
CONFIG_FSL_ENETC_QOS=y
CONFIG_NET_VENDOR_FUJITSU=y
CONFIG_PCMCIA_FMVJ18X=y
CONFIG_NET_VENDOR_FUNGIBLE=y
CONFIG_FUN_CORE=y
CONFIG_FUN_ETH=y
CONFIG_NET_VENDOR_GOOGLE=y
CONFIG_NET_VENDOR_HISILICON=y
CONFIG_HIX5HD2_GMAC=y
CONFIG_HISI_FEMAC=y
CONFIG_HIP04_ETH=y
CONFIG_HI13X1_GMAC=y
CONFIG_HNS_MDIO=y
CONFIG_HNS=y
CONFIG_HNS_DSAF=y
CONFIG_HNS_ENET=y
CONFIG_HNS3=y
CONFIG_HNS3_HCLGE=y
CONFIG_HNS3_DCB=y
CONFIG_HNS3_HCLGEVF=y
CONFIG_HNS3_ENET=y
CONFIG_NET_VENDOR_HUAWEI=y
CONFIG_NET_VENDOR_I825XX=y
CONFIG_NET_VENDOR_INTEL=y
CONFIG_E100=y
CONFIG_E1000=y
CONFIG_E1000E=y
CONFIG_IGB=y
CONFIG_IGB_HWMON=y
CONFIG_IGBVF=y
CONFIG_IXGB=y
CONFIG_IXGBE=y
CONFIG_IXGBE_HWMON=y
CONFIG_IXGBE_DCB=y
CONFIG_IXGBE_IPSEC=y
CONFIG_IXGBEVF=y
CONFIG_IXGBEVF_IPSEC=y
CONFIG_I40E=y
CONFIG_I40E_DCB=y
CONFIG_IAVF=y
CONFIG_I40EVF=y
CONFIG_ICE=y
CONFIG_ICE_SWITCHDEV=y
CONFIG_FM10K=y
CONFIG_IGC=y
CONFIG_NET_VENDOR_WANGXUN=y
CONFIG_NGBE=y
CONFIG_TXGBE=y
CONFIG_JME=y
CONFIG_KORINA=y
CONFIG_NET_VENDOR_ADI=y
CONFIG_ADIN1110=y
CONFIG_NET_VENDOR_LITEX=y
CONFIG_LITEX_LITEETH=y
CONFIG_NET_VENDOR_MARVELL=y
CONFIG_MV643XX_ETH=y
CONFIG_MVMDIO=y
CONFIG_MVNETA=y
CONFIG_MVPP2=y
CONFIG_MVPP2_PTP=y
CONFIG_PXA168_ETH=y
CONFIG_SKGE=y
CONFIG_SKGE_DEBUG=y
CONFIG_SKGE_GENESIS=y
CONFIG_SKY2=y
CONFIG_SKY2_DEBUG=y
CONFIG_OCTEONTX2_MBOX=y
CONFIG_OCTEONTX2_AF=y
CONFIG_NDC_DIS_DYNAMIC_CACHING=y
CONFIG_OCTEONTX2_PF=y
CONFIG_OCTEONTX2_VF=y
CONFIG_OCTEON_EP=y
CONFIG_PRESTERA=y
CONFIG_PRESTERA_PCI=y
CONFIG_NET_VENDOR_MEDIATEK=y
CONFIG_NET_MEDIATEK_SOC_WED=y
CONFIG_NET_MEDIATEK_SOC=y
CONFIG_NET_MEDIATEK_STAR_EMAC=y
CONFIG_NET_VENDOR_MELLANOX=y
CONFIG_MLX4_EN=y
CONFIG_MLX4_EN_DCB=y
CONFIG_MLX4_CORE=y
CONFIG_MLX4_DEBUG=y
CONFIG_MLX4_CORE_GEN2=y
CONFIG_MLX5_CORE=y
CONFIG_MLX5_FPGA=y
CONFIG_MLX5_CORE_EN=y
CONFIG_MLX5_EN_ARFS=y
CONFIG_MLX5_EN_RXNFC=y
CONFIG_MLX5_MPFS=y
CONFIG_MLX5_ESWITCH=y
CONFIG_MLX5_BRIDGE=y
CONFIG_MLX5_CLS_ACT=y
CONFIG_MLX5_TC_CT=y
CONFIG_MLX5_TC_SAMPLE=y
CONFIG_MLX5_CORE_EN_DCB=y
CONFIG_MLX5_CORE_IPOIB=y
CONFIG_MLX5_EN_MACSEC=y
CONFIG_MLX5_EN_IPSEC=y
CONFIG_MLX5_EN_TLS=y
CONFIG_MLX5_SW_STEERING=y
CONFIG_MLX5_SF=y
CONFIG_MLX5_SF_MANAGER=y
CONFIG_MLXSW_CORE=y
CONFIG_MLXSW_CORE_HWMON=y
CONFIG_MLXSW_CORE_THERMAL=y
CONFIG_MLXSW_PCI=y
CONFIG_MLXSW_I2C=y
CONFIG_MLXSW_SPECTRUM=y
CONFIG_MLXSW_SPECTRUM_DCB=y
CONFIG_MLXSW_MINIMAL=y
CONFIG_MLXFW=y
CONFIG_MLXBF_GIGE=y
CONFIG_NET_VENDOR_MICREL=y
CONFIG_KS8842=y
CONFIG_KS8851=y
CONFIG_KS8851_MLL=y
CONFIG_KSZ884X_PCI=y
CONFIG_NET_VENDOR_MICROCHIP=y
CONFIG_ENC28J60=y
CONFIG_ENC28J60_WRITEVERIFY=y
CONFIG_ENCX24J600=y
CONFIG_LAN743X=y
CONFIG_LAN966X_SWITCH=y
CONFIG_SPARX5_SWITCH=y
CONFIG_NET_VENDOR_MICROSEMI=y
CONFIG_MSCC_OCELOT_SWITCH_LIB=y
CONFIG_MSCC_OCELOT_SWITCH=y
CONFIG_NET_VENDOR_MICROSOFT=y
CONFIG_NET_VENDOR_MYRI=y
CONFIG_MYRI10GE=y
# CONFIG_FEALNX is not set
CONFIG_NET_VENDOR_NI=y
CONFIG_NI_XGE_MANAGEMENT_ENET=y
CONFIG_NET_VENDOR_NATSEMI=y
CONFIG_NATSEMI=y
CONFIG_NS83820=y
CONFIG_NET_VENDOR_NETERION=y
CONFIG_S2IO=y
CONFIG_NET_VENDOR_NETRONOME=y
CONFIG_NFP=y
CONFIG_NFP_APP_FLOWER=y
CONFIG_NFP_APP_ABM_NIC=y
CONFIG_NFP_DEBUG=y
CONFIG_NET_VENDOR_8390=y
CONFIG_PCMCIA_AXNET=y
CONFIG_AX88796=y
CONFIG_AX88796_93CX6=y
CONFIG_NE2K_PCI=y
CONFIG_PCMCIA_PCNET=y
CONFIG_NET_VENDOR_NVIDIA=y
CONFIG_FORCEDETH=y
CONFIG_LPC_ENET=y
CONFIG_NET_VENDOR_OKI=y
CONFIG_PCH_GBE=y
CONFIG_ETHOC=y
CONFIG_NET_VENDOR_PACKET_ENGINES=y
CONFIG_HAMACHI=y
CONFIG_YELLOWFIN=y
CONFIG_NET_VENDOR_PENSANDO=y
CONFIG_IONIC=y
CONFIG_NET_VENDOR_QLOGIC=y
CONFIG_QLA3XXX=y
CONFIG_QLCNIC=y
CONFIG_QLCNIC_SRIOV=y
CONFIG_QLCNIC_DCB=y
CONFIG_QLCNIC_HWMON=y
CONFIG_NETXEN_NIC=y
CONFIG_QED=y
CONFIG_QED_LL2=y
CONFIG_QED_SRIOV=y
CONFIG_QEDE=y
CONFIG_QED_RDMA=y
CONFIG_QED_ISCSI=y
CONFIG_QED_FCOE=y
CONFIG_QED_OOO=y
CONFIG_NET_VENDOR_BROCADE=y
CONFIG_BNA=y
CONFIG_NET_VENDOR_QUALCOMM=y
CONFIG_QCA7000=y
CONFIG_QCA7000_SPI=y
CONFIG_QCA7000_UART=y
CONFIG_QCOM_EMAC=y
CONFIG_RMNET=y
CONFIG_NET_VENDOR_RDC=y
CONFIG_R6040=y
CONFIG_NET_VENDOR_REALTEK=y
CONFIG_8139CP=y
CONFIG_8139TOO=y
CONFIG_8139TOO_PIO=y
CONFIG_8139TOO_TUNE_TWISTER=y
CONFIG_8139TOO_8129=y
CONFIG_8139_OLD_RX_RESET=y
CONFIG_R8169=y
CONFIG_NET_VENDOR_RENESAS=y
CONFIG_SH_ETH=y
CONFIG_RAVB=y
CONFIG_NET_VENDOR_ROCKER=y
CONFIG_ROCKER=y
CONFIG_NET_VENDOR_SAMSUNG=y
CONFIG_SXGBE_ETH=y
CONFIG_NET_VENDOR_SEEQ=y
CONFIG_NET_VENDOR_SILAN=y
CONFIG_SC92031=y
CONFIG_NET_VENDOR_SIS=y
CONFIG_SIS900=y
CONFIG_SIS190=y
CONFIG_NET_VENDOR_SOLARFLARE=y
CONFIG_SFC=y
CONFIG_SFC_MTD=y
CONFIG_SFC_MCDI_MON=y
CONFIG_SFC_SRIOV=y
CONFIG_SFC_MCDI_LOGGING=y
CONFIG_SFC_FALCON=y
CONFIG_SFC_FALCON_MTD=y
CONFIG_SFC_SIENA=y
CONFIG_SFC_SIENA_MTD=y
CONFIG_SFC_SIENA_MCDI_MON=y
CONFIG_SFC_SIENA_SRIOV=y
CONFIG_SFC_SIENA_MCDI_LOGGING=y
CONFIG_NET_VENDOR_SMSC=y
CONFIG_SMC91X=y
CONFIG_PCMCIA_SMC91C92=y
CONFIG_EPIC100=y
# CONFIG_SMC911X is not set
CONFIG_SMSC911X=y
CONFIG_SMSC9420=y
CONFIG_NET_VENDOR_SOCIONEXT=y
CONFIG_SNI_AVE=y
CONFIG_SNI_NETSEC=y
CONFIG_NET_VENDOR_STMICRO=y
CONFIG_STMMAC_ETH=y
CONFIG_STMMAC_SELFTESTS=y
CONFIG_STMMAC_PLATFORM=y
CONFIG_DWMAC_DWC_QOS_ETH=y
CONFIG_DWMAC_GENERIC=y
CONFIG_DWMAC_ANARION=y
CONFIG_DWMAC_INGENIC=y
CONFIG_DWMAC_IPQ806X=y
CONFIG_DWMAC_LPC18XX=y
CONFIG_DWMAC_MEDIATEK=y
CONFIG_DWMAC_MESON=y
CONFIG_DWMAC_OXNAS=y
CONFIG_DWMAC_QCOM_ETHQOS=y
CONFIG_DWMAC_ROCKCHIP=y
CONFIG_DWMAC_SOCFPGA=y
CONFIG_DWMAC_STI=y
CONFIG_DWMAC_STM32=y
CONFIG_DWMAC_SUNXI=y
CONFIG_DWMAC_SUN8I=y
CONFIG_DWMAC_IMX8=y
CONFIG_DWMAC_INTEL_PLAT=y
CONFIG_DWMAC_VISCONTI=y
CONFIG_DWMAC_LOONGSON=y
CONFIG_STMMAC_PCI=y
CONFIG_NET_VENDOR_SUN=y
CONFIG_HAPPYMEAL=y
CONFIG_SUNBMAC=y
CONFIG_SUNQE=y
CONFIG_SUNGEM=y
CONFIG_CASSINI=y
CONFIG_SUNVNET_COMMON=y
CONFIG_SUNVNET=y
CONFIG_LDMVSW=y
CONFIG_NIU=y
CONFIG_NET_VENDOR_SUNPLUS=y
CONFIG_SP7021_EMAC=y
CONFIG_NET_VENDOR_SYNOPSYS=y
CONFIG_DWC_XLGMAC=y
CONFIG_DWC_XLGMAC_PCI=y
CONFIG_NET_VENDOR_TEHUTI=y
CONFIG_TEHUTI=y
CONFIG_NET_VENDOR_TI=y
CONFIG_TI_DAVINCI_EMAC=y
CONFIG_TI_DAVINCI_MDIO=y
CONFIG_TI_CPSW_PHY_SEL=y
CONFIG_TI_CPSW=y
CONFIG_TI_CPSW_SWITCHDEV=y
CONFIG_TI_CPTS=y
CONFIG_TLAN=y
CONFIG_NET_VENDOR_VERTEXCOM=y
CONFIG_MSE102X=y
CONFIG_NET_VENDOR_VIA=y
CONFIG_VIA_RHINE=y
CONFIG_VIA_RHINE_MMIO=y
CONFIG_VIA_VELOCITY=y
CONFIG_NET_VENDOR_WIZNET=y
CONFIG_WIZNET_W5100=y
CONFIG_WIZNET_W5300=y
# CONFIG_WIZNET_BUS_DIRECT is not set
# CONFIG_WIZNET_BUS_INDIRECT is not set
CONFIG_WIZNET_BUS_ANY=y
CONFIG_WIZNET_W5100_SPI=y
CONFIG_NET_VENDOR_XILINX=y
CONFIG_XILINX_EMACLITE=y
CONFIG_XILINX_AXI_EMAC=y
CONFIG_XILINX_LL_TEMAC=y
CONFIG_NET_VENDOR_XIRCOM=y
CONFIG_PCMCIA_XIRC2PS=y
CONFIG_FDDI=y
CONFIG_DEFXX=y
CONFIG_SKFP=y
CONFIG_HIPPI=y
CONFIG_ROADRUNNER=y
CONFIG_ROADRUNNER_LARGE_RINGS=y
CONFIG_QCOM_IPA=y
CONFIG_PHYLINK=y
CONFIG_PHYLIB=y
CONFIG_SWPHY=y
CONFIG_LED_TRIGGER_PHY=y
CONFIG_FIXED_PHY=y
CONFIG_SFP=y

#
# MII PHY device drivers
#
CONFIG_AMD_PHY=y
CONFIG_MESON_GXL_PHY=y
CONFIG_ADIN_PHY=y
CONFIG_ADIN1100_PHY=y
CONFIG_AQUANTIA_PHY=y
CONFIG_AX88796B_PHY=y
CONFIG_BROADCOM_PHY=y
CONFIG_BCM54140_PHY=y
CONFIG_BCM63XX_PHY=y
CONFIG_BCM7XXX_PHY=y
CONFIG_BCM84881_PHY=y
CONFIG_BCM87XX_PHY=y
CONFIG_BCM_CYGNUS_PHY=y
CONFIG_BCM_NET_PHYLIB=y
CONFIG_BCM_NET_PHYPTP=y
CONFIG_CICADA_PHY=y
CONFIG_CORTINA_PHY=y
CONFIG_DAVICOM_PHY=y
CONFIG_ICPLUS_PHY=y
CONFIG_LXT_PHY=y
CONFIG_INTEL_XWAY_PHY=y
CONFIG_LSI_ET1011C_PHY=y
CONFIG_MARVELL_PHY=y
CONFIG_MARVELL_10G_PHY=y
CONFIG_MARVELL_88X2222_PHY=y
CONFIG_MAXLINEAR_GPHY=y
CONFIG_MEDIATEK_GE_PHY=y
CONFIG_MICREL_PHY=y
CONFIG_MICROCHIP_PHY=y
CONFIG_MICROCHIP_T1_PHY=y
CONFIG_MICROSEMI_PHY=y
CONFIG_MOTORCOMM_PHY=y
CONFIG_NATIONAL_PHY=y
CONFIG_NXP_C45_TJA11XX_PHY=y
CONFIG_NXP_TJA11XX_PHY=y
CONFIG_AT803X_PHY=y
CONFIG_QSEMI_PHY=y
CONFIG_REALTEK_PHY=y
CONFIG_RENESAS_PHY=y
CONFIG_ROCKCHIP_PHY=y
CONFIG_SMSC_PHY=y
CONFIG_STE10XP=y
CONFIG_TERANETICS_PHY=y
CONFIG_DP83822_PHY=y
CONFIG_DP83TC811_PHY=y
CONFIG_DP83848_PHY=y
CONFIG_DP83867_PHY=y
CONFIG_DP83869_PHY=y
CONFIG_DP83TD510_PHY=y
CONFIG_VITESSE_PHY=y
CONFIG_XILINX_GMII2RGMII=y
CONFIG_MICREL_KS8995MA=y
CONFIG_PSE_CONTROLLER=y
CONFIG_PSE_REGULATOR=y
CONFIG_CAN_DEV=y
CONFIG_CAN_VCAN=y
CONFIG_CAN_VXCAN=y
CONFIG_CAN_NETLINK=y
CONFIG_CAN_CALC_BITTIMING=y
CONFIG_CAN_RX_OFFLOAD=y
CONFIG_CAN_AT91=y
CONFIG_CAN_CAN327=y
CONFIG_CAN_FLEXCAN=y
CONFIG_CAN_GRCAN=y
CONFIG_CAN_JANZ_ICAN3=y
CONFIG_CAN_KVASER_PCIEFD=y
CONFIG_CAN_SLCAN=y
CONFIG_CAN_SUN4I=y
CONFIG_CAN_XILINXCAN=y
# CONFIG_PCH_CAN is not set
CONFIG_CAN_C_CAN=y
CONFIG_CAN_C_CAN_PLATFORM=y
CONFIG_CAN_C_CAN_PCI=y
CONFIG_CAN_CC770=y
CONFIG_CAN_CC770_ISA=y
CONFIG_CAN_CC770_PLATFORM=y
CONFIG_CAN_CTUCANFD=y
CONFIG_CAN_CTUCANFD_PCI=y
CONFIG_CAN_CTUCANFD_PLATFORM=y
CONFIG_CAN_IFI_CANFD=y
CONFIG_CAN_M_CAN=y
CONFIG_CAN_M_CAN_PCI=y
CONFIG_CAN_M_CAN_PLATFORM=y
CONFIG_CAN_M_CAN_TCAN4X5X=y
CONFIG_CAN_PEAK_PCIEFD=y
CONFIG_CAN_RCAR=y
CONFIG_CAN_RCAR_CANFD=y
CONFIG_CAN_SJA1000=y
CONFIG_CAN_EMS_PCI=y
CONFIG_CAN_EMS_PCMCIA=y
CONFIG_CAN_F81601=y
CONFIG_CAN_KVASER_PCI=y
CONFIG_CAN_PEAK_PCI=y
CONFIG_CAN_PEAK_PCIEC=y
CONFIG_CAN_PEAK_PCMCIA=y
CONFIG_CAN_PLX_PCI=y
CONFIG_CAN_SJA1000_ISA=y
CONFIG_CAN_SJA1000_PLATFORM=y
CONFIG_CAN_SOFTING=y
CONFIG_CAN_SOFTING_CS=y

#
# CAN SPI interfaces
#
CONFIG_CAN_HI311X=y
CONFIG_CAN_MCP251X=y
CONFIG_CAN_MCP251XFD=y
CONFIG_CAN_MCP251XFD_SANITY=y
# end of CAN SPI interfaces

#
# CAN USB interfaces
#
CONFIG_CAN_8DEV_USB=y
CONFIG_CAN_EMS_USB=y
CONFIG_CAN_ESD_USB=y
CONFIG_CAN_ETAS_ES58X=y
CONFIG_CAN_GS_USB=y
CONFIG_CAN_KVASER_USB=y
CONFIG_CAN_MCBA_USB=y
CONFIG_CAN_PEAK_USB=y
CONFIG_CAN_UCAN=y
# end of CAN USB interfaces

CONFIG_CAN_DEBUG_DEVICES=y

#
# MCTP Device Drivers
#
CONFIG_MCTP_SERIAL=y
CONFIG_MCTP_TRANSPORT_I2C=y
# end of MCTP Device Drivers

CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_BUS=y
CONFIG_FWNODE_MDIO=y
CONFIG_OF_MDIO=y
CONFIG_MDIO_DEVRES=y
CONFIG_MDIO_SUN4I=y
CONFIG_MDIO_XGENE=y
CONFIG_MDIO_ASPEED=y
CONFIG_MDIO_BITBANG=y
CONFIG_MDIO_BCM_IPROC=y
CONFIG_MDIO_BCM_UNIMAC=y
CONFIG_MDIO_CAVIUM=y
CONFIG_MDIO_GPIO=y
CONFIG_MDIO_HISI_FEMAC=y
CONFIG_MDIO_I2C=y
CONFIG_MDIO_MVUSB=y
CONFIG_MDIO_MSCC_MIIM=y
CONFIG_MDIO_MOXART=y
CONFIG_MDIO_OCTEON=y
CONFIG_MDIO_IPQ4019=y
CONFIG_MDIO_IPQ8064=y
CONFIG_MDIO_THUNDER=y

#
# MDIO Multiplexers
#
CONFIG_MDIO_BUS_MUX=y
CONFIG_MDIO_BUS_MUX_MESON_G12A=y
CONFIG_MDIO_BUS_MUX_BCM6368=y
CONFIG_MDIO_BUS_MUX_BCM_IPROC=y
CONFIG_MDIO_BUS_MUX_GPIO=y
CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y
CONFIG_MDIO_BUS_MUX_MMIOREG=y

#
# PCS device drivers
#
CONFIG_PCS_XPCS=y
CONFIG_PCS_LYNX=y
CONFIG_PCS_RZN1_MIIC=y
CONFIG_PCS_ALTERA_TSE=y
# end of PCS device drivers

CONFIG_PLIP=y
CONFIG_PPP=y
CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_DEFLATE=y
CONFIG_PPP_FILTER=y
CONFIG_PPP_MPPE=y
CONFIG_PPP_MULTILINK=y
CONFIG_PPPOATM=y
CONFIG_PPPOE=y
CONFIG_PPTP=y
CONFIG_PPPOL2TP=y
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
CONFIG_SLIP=y
CONFIG_SLHC=y
CONFIG_SLIP_COMPRESSED=y
CONFIG_SLIP_SMART=y
CONFIG_SLIP_MODE_SLIP6=y
CONFIG_USB_NET_DRIVERS=y
CONFIG_USB_CATC=y
CONFIG_USB_KAWETH=y
CONFIG_USB_PEGASUS=y
CONFIG_USB_RTL8150=y
CONFIG_USB_RTL8152=y
CONFIG_USB_LAN78XX=y
CONFIG_USB_USBNET=y
CONFIG_USB_NET_AX8817X=y
CONFIG_USB_NET_AX88179_178A=y
CONFIG_USB_NET_CDCETHER=y
CONFIG_USB_NET_CDC_EEM=y
CONFIG_USB_NET_CDC_NCM=y
CONFIG_USB_NET_HUAWEI_CDC_NCM=y
CONFIG_USB_NET_CDC_MBIM=y
CONFIG_USB_NET_DM9601=y
CONFIG_USB_NET_SR9700=y
CONFIG_USB_NET_SR9800=y
CONFIG_USB_NET_SMSC75XX=y
CONFIG_USB_NET_SMSC95XX=y
CONFIG_USB_NET_GL620A=y
CONFIG_USB_NET_NET1080=y
CONFIG_USB_NET_PLUSB=y
CONFIG_USB_NET_MCS7830=y
CONFIG_USB_NET_RNDIS_HOST=y
CONFIG_USB_NET_CDC_SUBSET_ENABLE=y
CONFIG_USB_NET_CDC_SUBSET=y
CONFIG_USB_ALI_M5632=y
CONFIG_USB_AN2720=y
CONFIG_USB_BELKIN=y
CONFIG_USB_ARMLINUX=y
CONFIG_USB_EPSON2888=y
CONFIG_USB_KC2190=y
CONFIG_USB_NET_ZAURUS=y
CONFIG_USB_NET_CX82310_ETH=y
CONFIG_USB_NET_KALMIA=y
CONFIG_USB_NET_QMI_WWAN=y
CONFIG_USB_HSO=y
CONFIG_USB_NET_INT51X1=y
CONFIG_USB_CDC_PHONET=y
CONFIG_USB_IPHETH=y
CONFIG_USB_SIERRA_NET=y
CONFIG_USB_VL600=y
CONFIG_USB_NET_CH9200=y
CONFIG_USB_NET_AQC111=y
CONFIG_USB_RTL8153_ECM=y
CONFIG_WLAN=y
CONFIG_WLAN_VENDOR_ADMTEK=y
CONFIG_ADM8211=y
CONFIG_ATH_COMMON=y
CONFIG_WLAN_VENDOR_ATH=y
CONFIG_ATH_DEBUG=y
CONFIG_ATH_TRACEPOINTS=y
CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS=y
CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING=y
CONFIG_ATH5K=y
CONFIG_ATH5K_DEBUG=y
CONFIG_ATH5K_TRACER=y
CONFIG_ATH5K_PCI=y
CONFIG_ATH5K_TEST_CHANNELS=y
CONFIG_ATH9K_HW=y
CONFIG_ATH9K_COMMON=y
CONFIG_ATH9K_COMMON_DEBUG=y
CONFIG_ATH9K_DFS_DEBUGFS=y
CONFIG_ATH9K_BTCOEX_SUPPORT=y
CONFIG_ATH9K=y
CONFIG_ATH9K_PCI=y
CONFIG_ATH9K_AHB=y
CONFIG_ATH9K_DEBUGFS=y
CONFIG_ATH9K_STATION_STATISTICS=y
CONFIG_ATH9K_TX99=y
CONFIG_ATH9K_DFS_CERTIFIED=y
CONFIG_ATH9K_DYNACK=y
CONFIG_ATH9K_WOW=y
CONFIG_ATH9K_RFKILL=y
CONFIG_ATH9K_CHANNEL_CONTEXT=y
CONFIG_ATH9K_PCOEM=y
CONFIG_ATH9K_PCI_NO_EEPROM=y
CONFIG_ATH9K_HTC=y
CONFIG_ATH9K_HTC_DEBUGFS=y
CONFIG_ATH9K_HWRNG=y
CONFIG_ATH9K_COMMON_SPECTRAL=y
CONFIG_CARL9170=y
CONFIG_CARL9170_LEDS=y
CONFIG_CARL9170_DEBUGFS=y
CONFIG_CARL9170_WPC=y
CONFIG_CARL9170_HWRNG=y
CONFIG_ATH6KL=y
CONFIG_ATH6KL_SDIO=y
CONFIG_ATH6KL_USB=y
CONFIG_ATH6KL_DEBUG=y
CONFIG_ATH6KL_TRACING=y
CONFIG_ATH6KL_REGDOMAIN=y
CONFIG_AR5523=y
CONFIG_WIL6210=y
CONFIG_WIL6210_ISR_COR=y
CONFIG_WIL6210_TRACING=y
CONFIG_WIL6210_DEBUGFS=y
CONFIG_ATH10K=y
CONFIG_ATH10K_CE=y
CONFIG_ATH10K_PCI=y
CONFIG_ATH10K_AHB=y
CONFIG_ATH10K_SDIO=y
CONFIG_ATH10K_USB=y
CONFIG_ATH10K_SNOC=y
CONFIG_ATH10K_DEBUG=y
CONFIG_ATH10K_DEBUGFS=y
CONFIG_ATH10K_SPECTRAL=y
CONFIG_ATH10K_TRACING=y
CONFIG_ATH10K_DFS_CERTIFIED=y
CONFIG_WCN36XX=y
CONFIG_WCN36XX_DEBUGFS=y
CONFIG_ATH11K=y
CONFIG_ATH11K_AHB=y
CONFIG_ATH11K_PCI=y
CONFIG_ATH11K_DEBUG=y
CONFIG_ATH11K_DEBUGFS=y
CONFIG_ATH11K_TRACING=y
CONFIG_ATH11K_SPECTRAL=y
CONFIG_WLAN_VENDOR_ATMEL=y
CONFIG_ATMEL=y
CONFIG_PCI_ATMEL=y
CONFIG_PCMCIA_ATMEL=y
CONFIG_AT76C50X_USB=y
CONFIG_WLAN_VENDOR_BROADCOM=y
CONFIG_B43=y
CONFIG_B43_BCMA=y
CONFIG_B43_SSB=y
CONFIG_B43_BUSES_BCMA_AND_SSB=y
# CONFIG_B43_BUSES_BCMA is not set
# CONFIG_B43_BUSES_SSB is not set
CONFIG_B43_PCI_AUTOSELECT=y
CONFIG_B43_PCICORE_AUTOSELECT=y
CONFIG_B43_SDIO=y
CONFIG_B43_BCMA_PIO=y
CONFIG_B43_PIO=y
CONFIG_B43_PHY_G=y
CONFIG_B43_PHY_N=y
CONFIG_B43_PHY_LP=y
CONFIG_B43_PHY_HT=y
CONFIG_B43_LEDS=y
CONFIG_B43_HWRNG=y
CONFIG_B43_DEBUG=y
CONFIG_B43LEGACY=y
CONFIG_B43LEGACY_PCI_AUTOSELECT=y
CONFIG_B43LEGACY_PCICORE_AUTOSELECT=y
CONFIG_B43LEGACY_LEDS=y
CONFIG_B43LEGACY_HWRNG=y
CONFIG_B43LEGACY_DEBUG=y
CONFIG_B43LEGACY_DMA=y
CONFIG_B43LEGACY_PIO=y
CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y
# CONFIG_B43LEGACY_DMA_MODE is not set
# CONFIG_B43LEGACY_PIO_MODE is not set
CONFIG_BRCMUTIL=y
CONFIG_BRCMSMAC=y
CONFIG_BRCMSMAC_LEDS=y
CONFIG_BRCMFMAC=y
CONFIG_BRCMFMAC_PROTO_BCDC=y
CONFIG_BRCMFMAC_PROTO_MSGBUF=y
CONFIG_BRCMFMAC_SDIO=y
CONFIG_BRCMFMAC_USB=y
CONFIG_BRCMFMAC_PCIE=y
CONFIG_BRCM_TRACING=y
CONFIG_BRCMDBG=y
CONFIG_WLAN_VENDOR_CISCO=y
CONFIG_AIRO_CS=y
CONFIG_WLAN_VENDOR_INTEL=y
CONFIG_IPW2100=y
CONFIG_IPW2100_MONITOR=y
CONFIG_IPW2100_DEBUG=y
CONFIG_IPW2200=y
CONFIG_IPW2200_MONITOR=y
CONFIG_IPW2200_RADIOTAP=y
CONFIG_IPW2200_PROMISCUOUS=y
CONFIG_IPW2200_QOS=y
CONFIG_IPW2200_DEBUG=y
CONFIG_LIBIPW=y
CONFIG_LIBIPW_DEBUG=y
CONFIG_IWLEGACY=y
CONFIG_IWL4965=y
CONFIG_IWL3945=y

#
# iwl3945 / iwl4965 Debugging Options
#
CONFIG_IWLEGACY_DEBUG=y
CONFIG_IWLEGACY_DEBUGFS=y
# end of iwl3945 / iwl4965 Debugging Options

CONFIG_IWLWIFI=y
CONFIG_IWLWIFI_LEDS=y
CONFIG_IWLDVM=y
CONFIG_IWLMVM=y

#
# Debugging Options
#
CONFIG_IWLWIFI_DEBUG=y
CONFIG_IWLWIFI_DEBUGFS=y
CONFIG_IWLWIFI_DEVICE_TRACING=y
# end of Debugging Options

CONFIG_WLAN_VENDOR_INTERSIL=y
CONFIG_HOSTAP=y
CONFIG_HOSTAP_FIRMWARE=y
CONFIG_HOSTAP_FIRMWARE_NVRAM=y
CONFIG_HOSTAP_PLX=y
CONFIG_HOSTAP_PCI=y
CONFIG_HOSTAP_CS=y
CONFIG_HERMES=y
CONFIG_HERMES_PRISM=y
CONFIG_HERMES_CACHE_FW_ON_INIT=y
CONFIG_PLX_HERMES=y
CONFIG_TMD_HERMES=y
CONFIG_NORTEL_HERMES=y
CONFIG_PCI_HERMES=y
CONFIG_PCMCIA_HERMES=y
CONFIG_PCMCIA_SPECTRUM=y
CONFIG_ORINOCO_USB=y
CONFIG_P54_COMMON=y
CONFIG_P54_USB=y
CONFIG_P54_PCI=y
CONFIG_P54_SPI=y
CONFIG_P54_SPI_DEFAULT_EEPROM=y
CONFIG_P54_LEDS=y
CONFIG_WLAN_VENDOR_MARVELL=y
CONFIG_LIBERTAS=y
CONFIG_LIBERTAS_USB=y
CONFIG_LIBERTAS_CS=y
CONFIG_LIBERTAS_SDIO=y
CONFIG_LIBERTAS_SPI=y
CONFIG_LIBERTAS_DEBUG=y
CONFIG_LIBERTAS_MESH=y
CONFIG_LIBERTAS_THINFIRM=y
CONFIG_LIBERTAS_THINFIRM_DEBUG=y
CONFIG_LIBERTAS_THINFIRM_USB=y
CONFIG_MWIFIEX=y
CONFIG_MWIFIEX_SDIO=y
CONFIG_MWIFIEX_PCIE=y
CONFIG_MWIFIEX_USB=y
CONFIG_MWL8K=y
CONFIG_WLAN_VENDOR_MEDIATEK=y
CONFIG_MT7601U=y
CONFIG_MT76_CORE=y
CONFIG_MT76_LEDS=y
CONFIG_MT76_USB=y
CONFIG_MT76_SDIO=y
CONFIG_MT76x02_LIB=y
CONFIG_MT76x02_USB=y
CONFIG_MT76_CONNAC_LIB=y
CONFIG_MT76x0_COMMON=y
CONFIG_MT76x0U=y
CONFIG_MT76x0E=y
CONFIG_MT76x2_COMMON=y
CONFIG_MT76x2E=y
CONFIG_MT76x2U=y
CONFIG_MT7603E=y
CONFIG_MT7615_COMMON=y
CONFIG_MT7615E=y
CONFIG_MT7622_WMAC=y
CONFIG_MT7663_USB_SDIO_COMMON=y
CONFIG_MT7663U=y
CONFIG_MT7663S=y
CONFIG_MT7915E=y
CONFIG_MT7986_WMAC=y
CONFIG_MT7921_COMMON=y
CONFIG_MT7921E=y
CONFIG_MT7921S=y
CONFIG_MT7921U=y
CONFIG_WLAN_VENDOR_MICROCHIP=y
CONFIG_WILC1000=y
CONFIG_WILC1000_SDIO=y
CONFIG_WILC1000_SPI=y
CONFIG_WILC1000_HW_OOB_INTR=y
CONFIG_WLAN_VENDOR_PURELIFI=y
CONFIG_PLFXLC=y
CONFIG_WLAN_VENDOR_RALINK=y
CONFIG_RT2X00=y
CONFIG_RT2400PCI=y
CONFIG_RT2500PCI=y
CONFIG_RT61PCI=y
CONFIG_RT2800PCI=y
CONFIG_RT2800PCI_RT33XX=y
CONFIG_RT2800PCI_RT35XX=y
CONFIG_RT2800PCI_RT53XX=y
CONFIG_RT2800PCI_RT3290=y
CONFIG_RT2500USB=y
CONFIG_RT73USB=y
CONFIG_RT2800USB=y
CONFIG_RT2800USB_RT33XX=y
CONFIG_RT2800USB_RT35XX=y
CONFIG_RT2800USB_RT3573=y
CONFIG_RT2800USB_RT53XX=y
CONFIG_RT2800USB_RT55XX=y
CONFIG_RT2800USB_UNKNOWN=y
CONFIG_RT2800_LIB=y
CONFIG_RT2800_LIB_MMIO=y
CONFIG_RT2X00_LIB_MMIO=y
CONFIG_RT2X00_LIB_PCI=y
CONFIG_RT2X00_LIB_USB=y
CONFIG_RT2X00_LIB=y
CONFIG_RT2X00_LIB_FIRMWARE=y
CONFIG_RT2X00_LIB_CRYPTO=y
CONFIG_RT2X00_LIB_LEDS=y
CONFIG_RT2X00_LIB_DEBUGFS=y
CONFIG_RT2X00_DEBUG=y
CONFIG_WLAN_VENDOR_REALTEK=y
CONFIG_RTL8180=y
CONFIG_RTL8187=y
CONFIG_RTL8187_LEDS=y
CONFIG_RTL_CARDS=y
CONFIG_RTL8192CE=y
CONFIG_RTL8192SE=y
CONFIG_RTL8192DE=y
CONFIG_RTL8723AE=y
CONFIG_RTL8723BE=y
CONFIG_RTL8188EE=y
CONFIG_RTL8192EE=y
CONFIG_RTL8821AE=y
CONFIG_RTL8192CU=y
CONFIG_RTLWIFI=y
CONFIG_RTLWIFI_PCI=y
CONFIG_RTLWIFI_USB=y
CONFIG_RTLWIFI_DEBUG=y
CONFIG_RTL8192C_COMMON=y
CONFIG_RTL8723_COMMON=y
CONFIG_RTLBTCOEXIST=y
CONFIG_RTL8XXXU=y
CONFIG_RTL8XXXU_UNTESTED=y
CONFIG_RTW88=y
CONFIG_RTW88_CORE=y
CONFIG_RTW88_PCI=y
CONFIG_RTW88_8822B=y
CONFIG_RTW88_8822C=y
CONFIG_RTW88_8723D=y
CONFIG_RTW88_8821C=y
CONFIG_RTW88_8822BE=y
CONFIG_RTW88_8822CE=y
CONFIG_RTW88_8723DE=y
CONFIG_RTW88_8821CE=y
CONFIG_RTW88_DEBUG=y
CONFIG_RTW88_DEBUGFS=y
CONFIG_RTW89=y
CONFIG_RTW89_CORE=y
CONFIG_RTW89_PCI=y
CONFIG_RTW89_8852A=y
CONFIG_RTW89_8852C=y
CONFIG_RTW89_8852AE=y
CONFIG_RTW89_8852CE=y
CONFIG_RTW89_DEBUG=y
CONFIG_RTW89_DEBUGMSG=y
CONFIG_RTW89_DEBUGFS=y
CONFIG_WLAN_VENDOR_RSI=y
CONFIG_RSI_91X=y
CONFIG_RSI_DEBUGFS=y
CONFIG_RSI_SDIO=y
CONFIG_RSI_USB=y
CONFIG_RSI_COEX=y
CONFIG_WLAN_VENDOR_SILABS=y
CONFIG_WFX=y
CONFIG_WLAN_VENDOR_ST=y
CONFIG_CW1200=y
CONFIG_CW1200_WLAN_SDIO=y
CONFIG_CW1200_WLAN_SPI=y
CONFIG_WLAN_VENDOR_TI=y
CONFIG_WL1251=y
CONFIG_WL1251_SPI=y
CONFIG_WL1251_SDIO=y
CONFIG_WL12XX=y
CONFIG_WL18XX=y
CONFIG_WLCORE=y
CONFIG_WLCORE_SPI=y
CONFIG_WLCORE_SDIO=y
CONFIG_WILINK_PLATFORM_DATA=y
CONFIG_WLAN_VENDOR_ZYDAS=y
CONFIG_USB_ZD1201=y
CONFIG_ZD1211RW=y
CONFIG_ZD1211RW_DEBUG=y
CONFIG_WLAN_VENDOR_QUANTENNA=y
CONFIG_QTNFMAC=y
CONFIG_QTNFMAC_PCIE=y
CONFIG_PCMCIA_RAYCS=y
CONFIG_PCMCIA_WL3501=y
CONFIG_MAC80211_HWSIM=y
CONFIG_USB_NET_RNDIS_WLAN=y
CONFIG_VIRT_WIFI=y
CONFIG_WAN=y
CONFIG_HDLC=y
CONFIG_HDLC_RAW=y
CONFIG_HDLC_RAW_ETH=y
CONFIG_HDLC_CISCO=y
CONFIG_HDLC_FR=y
CONFIG_HDLC_PPP=y
CONFIG_HDLC_X25=y
CONFIG_PCI200SYN=y
CONFIG_WANXL=y
CONFIG_PC300TOO=y
CONFIG_FARSYNC=y
CONFIG_FSL_UCC_HDLC=y
CONFIG_SLIC_DS26522=y
CONFIG_LAPBETHER=y
CONFIG_IEEE802154_DRIVERS=y
CONFIG_IEEE802154_FAKELB=y
CONFIG_IEEE802154_AT86RF230=y
CONFIG_IEEE802154_MRF24J40=y
CONFIG_IEEE802154_CC2520=y
CONFIG_IEEE802154_ATUSB=y
CONFIG_IEEE802154_ADF7242=y
CONFIG_IEEE802154_CA8210=y
CONFIG_IEEE802154_CA8210_DEBUGFS=y
CONFIG_IEEE802154_MCR20A=y
CONFIG_IEEE802154_HWSIM=y

#
# Wireless WAN
#
CONFIG_WWAN=y
CONFIG_WWAN_DEBUGFS=y
CONFIG_WWAN_HWSIM=y
CONFIG_MHI_WWAN_CTRL=y
CONFIG_MHI_WWAN_MBIM=y
CONFIG_QCOM_BAM_DMUX=y
CONFIG_RPMSG_WWAN_CTRL=y
CONFIG_IOSM=y
CONFIG_MTK_T7XX=y
# end of Wireless WAN

CONFIG_VMXNET3=y
CONFIG_USB4_NET=y
CONFIG_NETDEVSIM=y
CONFIG_NET_FAILOVER=y
CONFIG_ISDN=y
CONFIG_ISDN_CAPI=y
CONFIG_CAPI_TRACE=y
CONFIG_ISDN_CAPI_MIDDLEWARE=y
CONFIG_MISDN=y
CONFIG_MISDN_DSP=y
CONFIG_MISDN_L1OIP=y

#
# mISDN hardware drivers
#
CONFIG_MISDN_HFCPCI=y
CONFIG_MISDN_HFCMULTI=y
CONFIG_MISDN_HFCUSB=y
CONFIG_MISDN_AVMFRITZ=y
CONFIG_MISDN_SPEEDFAX=y
CONFIG_MISDN_INFINEON=y
CONFIG_MISDN_W6692=y
CONFIG_MISDN_NETJET=y
CONFIG_MISDN_HDLC=y
CONFIG_MISDN_IPAC=y
CONFIG_MISDN_ISAR=y

#
# Input device support
#
CONFIG_INPUT=y
CONFIG_INPUT_LEDS=y
CONFIG_INPUT_FF_MEMLESS=y
CONFIG_INPUT_SPARSEKMAP=y
CONFIG_INPUT_MATRIXKMAP=y
CONFIG_INPUT_VIVALDIFMAP=y

#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
CONFIG_INPUT_MOUSEDEV_PSAUX=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
CONFIG_INPUT_JOYDEV=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=y

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
CONFIG_KEYBOARD_ADC=y
CONFIG_KEYBOARD_ADP5520=y
CONFIG_KEYBOARD_ADP5588=y
CONFIG_KEYBOARD_ADP5589=y
CONFIG_KEYBOARD_ATKBD=y
CONFIG_KEYBOARD_QT1050=y
CONFIG_KEYBOARD_QT1070=y
CONFIG_KEYBOARD_QT2160=y
CONFIG_KEYBOARD_CLPS711X=y
CONFIG_KEYBOARD_DLINK_DIR685=y
CONFIG_KEYBOARD_LKKBD=y
CONFIG_KEYBOARD_EP93XX=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_GPIO_POLLED=y
CONFIG_KEYBOARD_TCA6416=y
CONFIG_KEYBOARD_TCA8418=y
CONFIG_KEYBOARD_MATRIX=y
CONFIG_KEYBOARD_LM8323=y
CONFIG_KEYBOARD_LM8333=y
CONFIG_KEYBOARD_MAX7359=y
CONFIG_KEYBOARD_MCS=y
CONFIG_KEYBOARD_MPR121=y
CONFIG_KEYBOARD_SNVS_PWRKEY=y
CONFIG_KEYBOARD_IMX=y
CONFIG_KEYBOARD_IMX_SC_KEY=y
CONFIG_KEYBOARD_NEWTON=y
CONFIG_KEYBOARD_OPENCORES=y
CONFIG_KEYBOARD_PINEPHONE=y
CONFIG_KEYBOARD_PMIC8XXX=y
CONFIG_KEYBOARD_SAMSUNG=y
CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
CONFIG_KEYBOARD_STOWAWAY=y
CONFIG_KEYBOARD_ST_KEYSCAN=y
CONFIG_KEYBOARD_SUNKBD=y
CONFIG_KEYBOARD_SH_KEYSC=y
CONFIG_KEYBOARD_STMPE=y
CONFIG_KEYBOARD_IQS62X=y
CONFIG_KEYBOARD_OMAP4=y
CONFIG_KEYBOARD_TC3589X=y
CONFIG_KEYBOARD_TM2_TOUCHKEY=y
CONFIG_KEYBOARD_TWL4030=y
CONFIG_KEYBOARD_XTKBD=y
CONFIG_KEYBOARD_CROS_EC=y
CONFIG_KEYBOARD_CAP11XX=y
CONFIG_KEYBOARD_BCM=y
CONFIG_KEYBOARD_MT6779=y
CONFIG_KEYBOARD_MTK_PMIC=y
CONFIG_KEYBOARD_CYPRESS_SF=y
CONFIG_INPUT_MOUSE=y
CONFIG_MOUSE_PS2=y
CONFIG_MOUSE_PS2_ALPS=y
CONFIG_MOUSE_PS2_BYD=y
CONFIG_MOUSE_PS2_LOGIPS2PP=y
CONFIG_MOUSE_PS2_SYNAPTICS=y
CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS=y
CONFIG_MOUSE_PS2_CYPRESS=y
CONFIG_MOUSE_PS2_TRACKPOINT=y
CONFIG_MOUSE_PS2_ELANTECH=y
CONFIG_MOUSE_PS2_ELANTECH_SMBUS=y
CONFIG_MOUSE_PS2_SENTELIC=y
CONFIG_MOUSE_PS2_TOUCHKIT=y
CONFIG_MOUSE_PS2_FOCALTECH=y
CONFIG_MOUSE_PS2_SMBUS=y
CONFIG_MOUSE_SERIAL=y
CONFIG_MOUSE_APPLETOUCH=y
CONFIG_MOUSE_BCM5974=y
CONFIG_MOUSE_CYAPA=y
CONFIG_MOUSE_ELAN_I2C=y
CONFIG_MOUSE_ELAN_I2C_I2C=y
CONFIG_MOUSE_ELAN_I2C_SMBUS=y
CONFIG_MOUSE_VSXXXAA=y
CONFIG_MOUSE_GPIO=y
CONFIG_MOUSE_SYNAPTICS_I2C=y
CONFIG_MOUSE_SYNAPTICS_USB=y
CONFIG_INPUT_JOYSTICK=y
CONFIG_JOYSTICK_ANALOG=y
CONFIG_JOYSTICK_A3D=y
CONFIG_JOYSTICK_ADC=y
CONFIG_JOYSTICK_ADI=y
CONFIG_JOYSTICK_COBRA=y
CONFIG_JOYSTICK_GF2K=y
CONFIG_JOYSTICK_GRIP=y
CONFIG_JOYSTICK_GRIP_MP=y
CONFIG_JOYSTICK_GUILLEMOT=y
CONFIG_JOYSTICK_INTERACT=y
CONFIG_JOYSTICK_SIDEWINDER=y
CONFIG_JOYSTICK_TMDC=y
CONFIG_JOYSTICK_IFORCE=y
CONFIG_JOYSTICK_IFORCE_USB=y
CONFIG_JOYSTICK_IFORCE_232=y
CONFIG_JOYSTICK_WARRIOR=y
CONFIG_JOYSTICK_MAGELLAN=y
CONFIG_JOYSTICK_SPACEORB=y
CONFIG_JOYSTICK_SPACEBALL=y
CONFIG_JOYSTICK_STINGER=y
CONFIG_JOYSTICK_TWIDJOY=y
CONFIG_JOYSTICK_ZHENHUA=y
CONFIG_JOYSTICK_DB9=y
CONFIG_JOYSTICK_GAMECON=y
CONFIG_JOYSTICK_TURBOGRAFX=y
CONFIG_JOYSTICK_AS5011=y
CONFIG_JOYSTICK_JOYDUMP=y
CONFIG_JOYSTICK_XPAD=y
CONFIG_JOYSTICK_XPAD_FF=y
CONFIG_JOYSTICK_XPAD_LEDS=y
CONFIG_JOYSTICK_WALKERA0701=y
CONFIG_JOYSTICK_PSXPAD_SPI=y
CONFIG_JOYSTICK_PSXPAD_SPI_FF=y
CONFIG_JOYSTICK_PXRC=y
CONFIG_JOYSTICK_QWIIC=y
CONFIG_JOYSTICK_FSIA6B=y
CONFIG_JOYSTICK_SENSEHAT=y
CONFIG_INPUT_TABLET=y
CONFIG_TABLET_USB_ACECAD=y
CONFIG_TABLET_USB_AIPTEK=y
CONFIG_TABLET_USB_HANWANG=y
CONFIG_TABLET_USB_KBTAB=y
CONFIG_TABLET_USB_PEGASUS=y
CONFIG_TABLET_SERIAL_WACOM4=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_88PM860X=y
CONFIG_TOUCHSCREEN_ADS7846=y
CONFIG_TOUCHSCREEN_AD7877=y
CONFIG_TOUCHSCREEN_AD7879=y
CONFIG_TOUCHSCREEN_AD7879_I2C=y
CONFIG_TOUCHSCREEN_AD7879_SPI=y
CONFIG_TOUCHSCREEN_ADC=y
CONFIG_TOUCHSCREEN_AR1021_I2C=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
CONFIG_TOUCHSCREEN_ATMEL_MXT_T37=y
CONFIG_TOUCHSCREEN_AUO_PIXCIR=y
CONFIG_TOUCHSCREEN_BU21013=y
CONFIG_TOUCHSCREEN_BU21029=y
CONFIG_TOUCHSCREEN_CHIPONE_ICN8318=y
CONFIG_TOUCHSCREEN_CY8CTMA140=y
CONFIG_TOUCHSCREEN_CY8CTMG110=y
CONFIG_TOUCHSCREEN_CYTTSP_CORE=y
CONFIG_TOUCHSCREEN_CYTTSP_I2C=y
CONFIG_TOUCHSCREEN_CYTTSP_SPI=y
CONFIG_TOUCHSCREEN_CYTTSP4_CORE=y
CONFIG_TOUCHSCREEN_CYTTSP4_I2C=y
CONFIG_TOUCHSCREEN_CYTTSP4_SPI=y
CONFIG_TOUCHSCREEN_DA9034=y
CONFIG_TOUCHSCREEN_DA9052=y
CONFIG_TOUCHSCREEN_DYNAPRO=y
CONFIG_TOUCHSCREEN_HAMPSHIRE=y
CONFIG_TOUCHSCREEN_EETI=y
CONFIG_TOUCHSCREEN_EGALAX=y
CONFIG_TOUCHSCREEN_EGALAX_SERIAL=y
CONFIG_TOUCHSCREEN_EXC3000=y
CONFIG_TOUCHSCREEN_FUJITSU=y
CONFIG_TOUCHSCREEN_GOODIX=y
CONFIG_TOUCHSCREEN_HIDEEP=y
CONFIG_TOUCHSCREEN_HYCON_HY46XX=y
CONFIG_TOUCHSCREEN_ILI210X=y
CONFIG_TOUCHSCREEN_ILITEK=y
CONFIG_TOUCHSCREEN_IPROC=y
CONFIG_TOUCHSCREEN_S6SY761=y
CONFIG_TOUCHSCREEN_GUNZE=y
CONFIG_TOUCHSCREEN_EKTF2127=y
CONFIG_TOUCHSCREEN_ELAN=y
CONFIG_TOUCHSCREEN_ELO=y
CONFIG_TOUCHSCREEN_WACOM_W8001=y
CONFIG_TOUCHSCREEN_WACOM_I2C=y
CONFIG_TOUCHSCREEN_MAX11801=y
CONFIG_TOUCHSCREEN_MCS5000=y
CONFIG_TOUCHSCREEN_MMS114=y
CONFIG_TOUCHSCREEN_MELFAS_MIP4=y
CONFIG_TOUCHSCREEN_MSG2638=y
CONFIG_TOUCHSCREEN_MTOUCH=y
CONFIG_TOUCHSCREEN_IMAGIS=y
CONFIG_TOUCHSCREEN_IMX6UL_TSC=y
CONFIG_TOUCHSCREEN_INEXIO=y
CONFIG_TOUCHSCREEN_MK712=y
CONFIG_TOUCHSCREEN_PENMOUNT=y
CONFIG_TOUCHSCREEN_EDT_FT5X06=y
CONFIG_TOUCHSCREEN_RASPBERRYPI_FW=y
CONFIG_TOUCHSCREEN_MIGOR=y
CONFIG_TOUCHSCREEN_TOUCHRIGHT=y
CONFIG_TOUCHSCREEN_TOUCHWIN=y
CONFIG_TOUCHSCREEN_TI_AM335X_TSC=y
CONFIG_TOUCHSCREEN_UCB1400=y
CONFIG_TOUCHSCREEN_PIXCIR=y
CONFIG_TOUCHSCREEN_WDT87XX_I2C=y
CONFIG_TOUCHSCREEN_WM831X=y
CONFIG_TOUCHSCREEN_WM97XX=y
CONFIG_TOUCHSCREEN_WM9705=y
CONFIG_TOUCHSCREEN_WM9712=y
CONFIG_TOUCHSCREEN_WM9713=y
CONFIG_TOUCHSCREEN_USB_COMPOSITE=y
CONFIG_TOUCHSCREEN_MXS_LRADC=y
CONFIG_TOUCHSCREEN_MX25=y
CONFIG_TOUCHSCREEN_MC13783=y
CONFIG_TOUCHSCREEN_USB_EGALAX=y
CONFIG_TOUCHSCREEN_USB_PANJIT=y
CONFIG_TOUCHSCREEN_USB_3M=y
CONFIG_TOUCHSCREEN_USB_ITM=y
CONFIG_TOUCHSCREEN_USB_ETURBO=y
CONFIG_TOUCHSCREEN_USB_GUNZE=y
CONFIG_TOUCHSCREEN_USB_DMC_TSC10=y
CONFIG_TOUCHSCREEN_USB_IRTOUCH=y
CONFIG_TOUCHSCREEN_USB_IDEALTEK=y
CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH=y
CONFIG_TOUCHSCREEN_USB_GOTOP=y
CONFIG_TOUCHSCREEN_USB_JASTEC=y
CONFIG_TOUCHSCREEN_USB_ELO=y
CONFIG_TOUCHSCREEN_USB_E2I=y
CONFIG_TOUCHSCREEN_USB_ZYTRONIC=y
CONFIG_TOUCHSCREEN_USB_ETT_TC45USB=y
CONFIG_TOUCHSCREEN_USB_NEXIO=y
CONFIG_TOUCHSCREEN_USB_EASYTOUCH=y
CONFIG_TOUCHSCREEN_TOUCHIT213=y
CONFIG_TOUCHSCREEN_TS4800=y
CONFIG_TOUCHSCREEN_TSC_SERIO=y
CONFIG_TOUCHSCREEN_TSC200X_CORE=y
CONFIG_TOUCHSCREEN_TSC2004=y
CONFIG_TOUCHSCREEN_TSC2005=y
CONFIG_TOUCHSCREEN_TSC2007=y
CONFIG_TOUCHSCREEN_TSC2007_IIO=y
CONFIG_TOUCHSCREEN_PCAP=y
CONFIG_TOUCHSCREEN_RM_TS=y
CONFIG_TOUCHSCREEN_SILEAD=y
CONFIG_TOUCHSCREEN_SIS_I2C=y
CONFIG_TOUCHSCREEN_ST1232=y
CONFIG_TOUCHSCREEN_STMFTS=y
CONFIG_TOUCHSCREEN_STMPE=y
CONFIG_TOUCHSCREEN_SUN4I=y
CONFIG_TOUCHSCREEN_SUR40=y
CONFIG_TOUCHSCREEN_SURFACE3_SPI=y
CONFIG_TOUCHSCREEN_SX8654=y
CONFIG_TOUCHSCREEN_TPS6507X=y
CONFIG_TOUCHSCREEN_ZET6223=y
CONFIG_TOUCHSCREEN_ZFORCE=y
CONFIG_TOUCHSCREEN_COLIBRI_VF50=y
CONFIG_TOUCHSCREEN_ROHM_BU21023=y
CONFIG_TOUCHSCREEN_IQS5XX=y
CONFIG_TOUCHSCREEN_ZINITIX=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_88PM860X_ONKEY=y
CONFIG_INPUT_88PM80X_ONKEY=y
CONFIG_INPUT_AD714X=y
CONFIG_INPUT_AD714X_I2C=y
CONFIG_INPUT_AD714X_SPI=y
CONFIG_INPUT_ARIEL_PWRBUTTON=y
CONFIG_INPUT_ARIZONA_HAPTICS=y
CONFIG_INPUT_ATC260X_ONKEY=y
CONFIG_INPUT_ATMEL_CAPTOUCH=y
CONFIG_INPUT_BMA150=y
CONFIG_INPUT_E3X0_BUTTON=y
CONFIG_INPUT_PM8941_PWRKEY=y
CONFIG_INPUT_PM8XXX_VIBRATOR=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
CONFIG_INPUT_SPARCSPKR=y
CONFIG_INPUT_MAX77650_ONKEY=y
CONFIG_INPUT_MAX77693_HAPTIC=y
CONFIG_INPUT_MAX8925_ONKEY=y
CONFIG_INPUT_MAX8997_HAPTIC=y
CONFIG_INPUT_MC13783_PWRBUTTON=y
CONFIG_INPUT_MMA8450=y
CONFIG_INPUT_GPIO_BEEPER=y
CONFIG_INPUT_GPIO_DECODER=y
CONFIG_INPUT_GPIO_VIBRA=y
CONFIG_INPUT_CPCAP_PWRBUTTON=y
CONFIG_INPUT_ATI_REMOTE2=y
CONFIG_INPUT_KEYSPAN_REMOTE=y
CONFIG_INPUT_KXTJ9=y
CONFIG_INPUT_POWERMATE=y
CONFIG_INPUT_YEALINK=y
CONFIG_INPUT_CM109=y
CONFIG_INPUT_REGULATOR_HAPTIC=y
CONFIG_INPUT_RETU_PWRBUTTON=y
CONFIG_INPUT_TPS65218_PWRBUTTON=y
CONFIG_INPUT_AXP20X_PEK=y
CONFIG_INPUT_TWL4030_PWRBUTTON=y
CONFIG_INPUT_TWL4030_VIBRA=y
CONFIG_INPUT_TWL6040_VIBRA=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_PALMAS_PWRBUTTON=y
CONFIG_INPUT_PCF50633_PMU=y
CONFIG_INPUT_PCF8574=y
CONFIG_INPUT_PWM_BEEPER=y
CONFIG_INPUT_PWM_VIBRA=y
CONFIG_INPUT_RK805_PWRKEY=y
CONFIG_INPUT_GPIO_ROTARY_ENCODER=y
CONFIG_INPUT_DA7280_HAPTICS=y
CONFIG_INPUT_DA9052_ONKEY=y
CONFIG_INPUT_DA9055_ONKEY=y
CONFIG_INPUT_DA9063_ONKEY=y
CONFIG_INPUT_WM831X_ON=y
CONFIG_INPUT_PCAP=y
CONFIG_INPUT_ADXL34X=y
CONFIG_INPUT_ADXL34X_I2C=y
CONFIG_INPUT_ADXL34X_SPI=y
CONFIG_INPUT_IBM_PANEL=y
CONFIG_INPUT_IMS_PCU=y
CONFIG_INPUT_IQS269A=y
CONFIG_INPUT_IQS626A=y
CONFIG_INPUT_IQS7222=y
CONFIG_INPUT_CMA3000=y
CONFIG_INPUT_CMA3000_I2C=y
CONFIG_INPUT_IDEAPAD_SLIDEBAR=y
CONFIG_INPUT_DRV260X_HAPTICS=y
CONFIG_INPUT_DRV2665_HAPTICS=y
CONFIG_INPUT_DRV2667_HAPTICS=y
CONFIG_INPUT_HISI_POWERKEY=y
CONFIG_INPUT_RAVE_SP_PWRBUTTON=y
CONFIG_INPUT_SC27XX_VIBRA=y
CONFIG_INPUT_RT5120_PWRKEY=y
CONFIG_INPUT_STPMIC1_ONKEY=y
CONFIG_RMI4_CORE=y
CONFIG_RMI4_I2C=y
CONFIG_RMI4_SPI=y
CONFIG_RMI4_SMB=y
CONFIG_RMI4_F03=y
CONFIG_RMI4_F03_SERIO=y
CONFIG_RMI4_2D_SENSOR=y
CONFIG_RMI4_F11=y
CONFIG_RMI4_F12=y
CONFIG_RMI4_F30=y
CONFIG_RMI4_F34=y
CONFIG_RMI4_F3A=y
CONFIG_RMI4_F54=y
CONFIG_RMI4_F55=y

#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
CONFIG_SERIO_I8042=y
CONFIG_SERIO_SERPORT=y
CONFIG_SERIO_PARKBD=y
CONFIG_SERIO_PCIPS2=y
CONFIG_SERIO_LIBPS2=y
CONFIG_SERIO_RAW=y
CONFIG_SERIO_ALTERA_PS2=y
CONFIG_SERIO_PS2MULT=y
CONFIG_SERIO_ARC_PS2=y
CONFIG_SERIO_APBPS2=y
CONFIG_SERIO_OLPC_APSP=y
CONFIG_SERIO_SUN4I_PS2=y
CONFIG_SERIO_GPIO_PS2=y
CONFIG_USERIO=y
CONFIG_GAMEPORT=y
CONFIG_GAMEPORT_NS558=y
CONFIG_GAMEPORT_L4=y
CONFIG_GAMEPORT_EMU10K1=y
CONFIG_GAMEPORT_FM801=y
# end of Hardware I/O ports
# end of Input device support

#
# Character devices
#
CONFIG_TTY=y
CONFIG_VT=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_VT_CONSOLE_SLEEP=y
CONFIG_HW_CONSOLE=y
CONFIG_VT_HW_CONSOLE_BINDING=y
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
CONFIG_LDISC_AUTOLOAD=y

#
# Serial drivers
#
CONFIG_SERIAL_EARLYCON=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
CONFIG_SERIAL_8250_16550A_VARIANTS=y
CONFIG_SERIAL_8250_FINTEK=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_DMA=y
CONFIG_SERIAL_8250_PCI=y
CONFIG_SERIAL_8250_EXAR=y
CONFIG_SERIAL_8250_CS=y
CONFIG_SERIAL_8250_MEN_MCB=y
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_ASPEED_VUART=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_8250_DETECT_IRQ=y
CONFIG_SERIAL_8250_RSA=y
CONFIG_SERIAL_8250_DWLIB=y
CONFIG_SERIAL_8250_BCM2835AUX=y
CONFIG_SERIAL_8250_FSL=y
CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_8250_EM=y
CONFIG_SERIAL_8250_IOC3=y
CONFIG_SERIAL_8250_RT288X=y
CONFIG_SERIAL_8250_OMAP=y
CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP=y
CONFIG_SERIAL_8250_LPC18XX=y
CONFIG_SERIAL_8250_MT6577=y
CONFIG_SERIAL_8250_UNIPHIER=y
CONFIG_SERIAL_8250_INGENIC=y
CONFIG_SERIAL_8250_LPSS=y
CONFIG_SERIAL_8250_MID=y
CONFIG_SERIAL_8250_PERICOM=y
CONFIG_SERIAL_8250_PXA=y
CONFIG_SERIAL_8250_TEGRA=y
CONFIG_SERIAL_8250_BCM7271=y
CONFIG_SERIAL_OF_PLATFORM=y

#
# Non-8250 serial port support
#
CONFIG_SERIAL_AMBA_PL010=y
CONFIG_SERIAL_AMBA_PL010_CONSOLE=y
CONFIG_SERIAL_ATMEL=y
CONFIG_SERIAL_ATMEL_CONSOLE=y
CONFIG_SERIAL_ATMEL_PDC=y
CONFIG_SERIAL_ATMEL_TTYAT=y
CONFIG_SERIAL_KGDB_NMI=y
CONFIG_SERIAL_MESON=y
CONFIG_SERIAL_MESON_CONSOLE=y
CONFIG_SERIAL_CLPS711X=y
CONFIG_SERIAL_CLPS711X_CONSOLE=y
CONFIG_SERIAL_SAMSUNG=y
CONFIG_SERIAL_SAMSUNG_UARTS_4=y
CONFIG_SERIAL_SAMSUNG_UARTS=4
CONFIG_SERIAL_SAMSUNG_CONSOLE=y
CONFIG_SERIAL_TEGRA=y
CONFIG_SERIAL_TEGRA_TCU=y
CONFIG_SERIAL_TEGRA_TCU_CONSOLE=y
CONFIG_SERIAL_MAX3100=y
CONFIG_SERIAL_MAX310X=y
CONFIG_SERIAL_IMX=y
CONFIG_SERIAL_IMX_CONSOLE=y
CONFIG_SERIAL_IMX_EARLYCON=y
CONFIG_SERIAL_UARTLITE=y
CONFIG_SERIAL_UARTLITE_CONSOLE=y
CONFIG_SERIAL_UARTLITE_NR_UARTS=1
CONFIG_SERIAL_SUNCORE=y
CONFIG_SERIAL_SUNZILOG=y
CONFIG_SERIAL_SUNZILOG_CONSOLE=y
CONFIG_SERIAL_SUNSU=y
CONFIG_SERIAL_SUNSU_CONSOLE=y
CONFIG_SERIAL_SUNSAB=y
CONFIG_SERIAL_SUNSAB_CONSOLE=y
CONFIG_SERIAL_SUNHV=y
CONFIG_SERIAL_SH_SCI=y
CONFIG_SERIAL_SH_SCI_NR_UARTS=2
CONFIG_SERIAL_SH_SCI_CONSOLE=y
CONFIG_SERIAL_SH_SCI_EARLYCON=y
CONFIG_SERIAL_SH_SCI_DMA=y
CONFIG_SERIAL_HS_LPC32XX=y
CONFIG_SERIAL_HS_LPC32XX_CONSOLE=y
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_CONSOLE_POLL=y
CONFIG_SERIAL_ICOM=y
CONFIG_SERIAL_JSM=y
CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_QCOM_GENI=y
CONFIG_SERIAL_QCOM_GENI_CONSOLE=y
CONFIG_SERIAL_VT8500=y
CONFIG_SERIAL_VT8500_CONSOLE=y
CONFIG_SERIAL_OMAP=y
CONFIG_SERIAL_OMAP_CONSOLE=y
CONFIG_SERIAL_SIFIVE=y
CONFIG_SERIAL_SIFIVE_CONSOLE=y
CONFIG_SERIAL_LANTIQ=y
CONFIG_SERIAL_LANTIQ_CONSOLE=y
CONFIG_SERIAL_QE=y
CONFIG_SERIAL_SCCNXP=y
CONFIG_SERIAL_SCCNXP_CONSOLE=y
CONFIG_SERIAL_SC16IS7XX_CORE=y
CONFIG_SERIAL_SC16IS7XX=y
CONFIG_SERIAL_SC16IS7XX_I2C=y
CONFIG_SERIAL_SC16IS7XX_SPI=y
CONFIG_SERIAL_TIMBERDALE=y
CONFIG_SERIAL_BCM63XX=y
CONFIG_SERIAL_BCM63XX_CONSOLE=y
CONFIG_SERIAL_GRLIB_GAISLER_APBUART=y
CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE=y
CONFIG_SERIAL_ALTERA_JTAGUART=y
CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE=y
CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS=y
CONFIG_SERIAL_ALTERA_UART=y
CONFIG_SERIAL_ALTERA_UART_MAXPORTS=4
CONFIG_SERIAL_ALTERA_UART_BAUDRATE=115200
CONFIG_SERIAL_ALTERA_UART_CONSOLE=y
CONFIG_SERIAL_PCH_UART=y
CONFIG_SERIAL_PCH_UART_CONSOLE=y
CONFIG_SERIAL_MXS_AUART=y
CONFIG_SERIAL_MXS_AUART_CONSOLE=y
CONFIG_SERIAL_XILINX_PS_UART=y
CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
CONFIG_SERIAL_MPS2_UART_CONSOLE=y
CONFIG_SERIAL_MPS2_UART=y
CONFIG_SERIAL_ARC=y
CONFIG_SERIAL_ARC_CONSOLE=y
CONFIG_SERIAL_ARC_NR_PORTS=1
CONFIG_SERIAL_RP2=y
CONFIG_SERIAL_RP2_NR_UARTS=32
CONFIG_SERIAL_FSL_LPUART=y
CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
CONFIG_SERIAL_FSL_LINFLEXUART=y
CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE=y
CONFIG_SERIAL_CONEXANT_DIGICOLOR=y
CONFIG_SERIAL_CONEXANT_DIGICOLOR_CONSOLE=y
CONFIG_SERIAL_ST_ASC=y
CONFIG_SERIAL_ST_ASC_CONSOLE=y
CONFIG_SERIAL_MEN_Z135=y
CONFIG_SERIAL_SPRD=y
CONFIG_SERIAL_SPRD_CONSOLE=y
CONFIG_SERIAL_STM32=y
CONFIG_SERIAL_STM32_CONSOLE=y
CONFIG_SERIAL_MVEBU_UART=y
CONFIG_SERIAL_MVEBU_CONSOLE=y
CONFIG_SERIAL_OWL=y
CONFIG_SERIAL_OWL_CONSOLE=y
CONFIG_SERIAL_RDA=y
CONFIG_SERIAL_RDA_CONSOLE=y
CONFIG_SERIAL_MILBEAUT_USIO=y
CONFIG_SERIAL_MILBEAUT_USIO_PORTS=4
CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE=y
CONFIG_SERIAL_LITEUART=y
CONFIG_SERIAL_LITEUART_MAX_PORTS=1
CONFIG_SERIAL_LITEUART_CONSOLE=y
CONFIG_SERIAL_SUNPLUS=y
CONFIG_SERIAL_SUNPLUS_CONSOLE=y
# end of Serial drivers

CONFIG_SERIAL_MCTRL_GPIO=y
CONFIG_SERIAL_NONSTANDARD=y
CONFIG_MOXA_INTELLIO=y
CONFIG_MOXA_SMARTIO=y
CONFIG_SYNCLINK_GT=y
CONFIG_N_HDLC=y
CONFIG_GOLDFISH_TTY=y
CONFIG_GOLDFISH_TTY_EARLY_CONSOLE=y
CONFIG_N_GSM=y
CONFIG_NOZOMI=y
CONFIG_NULL_TTY=y
CONFIG_VCC=y
CONFIG_HVC_DRIVER=y
CONFIG_RPMSG_TTY=y
CONFIG_SERIAL_DEV_BUS=y
CONFIG_SERIAL_DEV_CTRL_TTYPORT=y
CONFIG_TTY_PRINTK=y
CONFIG_TTY_PRINTK_LEVEL=6
CONFIG_PRINTER=y
CONFIG_LP_CONSOLE=y
CONFIG_PPDEV=y
CONFIG_VIRTIO_CONSOLE=y
CONFIG_IPMI_HANDLER=y
CONFIG_IPMI_PLAT_DATA=y
CONFIG_IPMI_PANIC_EVENT=y
CONFIG_IPMI_PANIC_STRING=y
CONFIG_IPMI_DEVICE_INTERFACE=y
CONFIG_IPMI_SI=y
CONFIG_IPMI_SSIF=y
CONFIG_IPMI_IPMB=y
CONFIG_IPMI_WATCHDOG=y
CONFIG_IPMI_POWEROFF=y
CONFIG_IPMI_KCS_BMC=y
CONFIG_ASPEED_KCS_IPMI_BMC=y
CONFIG_NPCM7XX_KCS_IPMI_BMC=y
CONFIG_IPMI_KCS_BMC_CDEV_IPMI=y
CONFIG_IPMI_KCS_BMC_SERIO=y
CONFIG_ASPEED_BT_IPMI_BMC=y
CONFIG_IPMB_DEVICE_INTERFACE=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_TIMERIOMEM=y
CONFIG_HW_RANDOM_ATMEL=y
CONFIG_HW_RANDOM_BA431=y
CONFIG_HW_RANDOM_BCM2835=y
CONFIG_HW_RANDOM_IPROC_RNG200=y
CONFIG_HW_RANDOM_N2RNG=y
CONFIG_HW_RANDOM_IXP4XX=y
CONFIG_HW_RANDOM_OMAP=y
CONFIG_HW_RANDOM_OMAP3_ROM=y
CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_HW_RANDOM_IMX_RNGC=y
CONFIG_HW_RANDOM_NOMADIK=y
CONFIG_HW_RANDOM_STM32=y
CONFIG_HW_RANDOM_POLARFIRE_SOC=y
CONFIG_HW_RANDOM_MESON=y
CONFIG_HW_RANDOM_MTK=y
CONFIG_HW_RANDOM_EXYNOS=y
CONFIG_HW_RANDOM_NPCM=y
CONFIG_HW_RANDOM_KEYSTONE=y
CONFIG_HW_RANDOM_CCTRNG=y
CONFIG_HW_RANDOM_XIPHERA=y
CONFIG_HW_RANDOM_CN10K=y
CONFIG_APPLICOM=y

#
# PCMCIA character devices
#
CONFIG_SYNCLINK_CS=y
CONFIG_CARDMAN_4000=y
CONFIG_CARDMAN_4040=y
CONFIG_SCR24X=y
CONFIG_IPWIRELESS=y
# end of PCMCIA character devices

CONFIG_DEVMEM=y
CONFIG_DEVPORT=y
CONFIG_TCG_TPM=y
CONFIG_HW_RANDOM_TPM=y
CONFIG_TCG_TIS_CORE=y
CONFIG_TCG_TIS=y
CONFIG_TCG_TIS_SPI=y
CONFIG_TCG_TIS_SPI_CR50=y
CONFIG_TCG_TIS_I2C=y
CONFIG_TCG_TIS_SYNQUACER=y
CONFIG_TCG_TIS_I2C_CR50=y
CONFIG_TCG_TIS_I2C_ATMEL=y
CONFIG_TCG_TIS_I2C_INFINEON=y
CONFIG_TCG_TIS_I2C_NUVOTON=y
CONFIG_TCG_ATMEL=y
CONFIG_TCG_VTPM_PROXY=y
CONFIG_TCG_TIS_ST33ZP24=y
CONFIG_TCG_TIS_ST33ZP24_I2C=y
CONFIG_TCG_TIS_ST33ZP24_SPI=y
CONFIG_XILLYBUS_CLASS=y
CONFIG_XILLYBUS=y
CONFIG_XILLYBUS_PCIE=y
CONFIG_XILLYBUS_OF=y
CONFIG_XILLYUSB=y
CONFIG_ADI=y
CONFIG_RANDOM_TRUST_CPU=y
CONFIG_RANDOM_TRUST_BOOTLOADER=y
# end of Character devices

#
# I2C support
#
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_COMPAT=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MUX=y

#
# Multiplexer I2C Chip support
#
CONFIG_I2C_ARB_GPIO_CHALLENGE=y
CONFIG_I2C_MUX_GPIO=y
CONFIG_I2C_MUX_GPMUX=y
CONFIG_I2C_MUX_LTC4306=y
CONFIG_I2C_MUX_PCA9541=y
CONFIG_I2C_MUX_PCA954x=y
CONFIG_I2C_MUX_PINCTRL=y
CONFIG_I2C_MUX_REG=y
CONFIG_I2C_DEMUX_PINCTRL=y
CONFIG_I2C_MUX_MLXCPLD=y
# end of Multiplexer I2C Chip support

CONFIG_I2C_HELPER_AUTO=y
CONFIG_I2C_SMBUS=y
CONFIG_I2C_ALGOBIT=y
CONFIG_I2C_ALGOPCA=y

#
# I2C Hardware Bus support
#

#
# PC SMBus host controller drivers
#
CONFIG_I2C_CCGX_UCSI=y
CONFIG_I2C_ALI1535=y
CONFIG_I2C_ALI1563=y
CONFIG_I2C_ALI15X3=y
CONFIG_I2C_AMD756=y
CONFIG_I2C_AMD8111=y
CONFIG_I2C_HIX5HD2=y
CONFIG_I2C_I801=y
CONFIG_I2C_ISCH=y
CONFIG_I2C_PIIX4=y
CONFIG_I2C_NFORCE2=y
CONFIG_I2C_NVIDIA_GPU=y
CONFIG_I2C_SIS5595=y
CONFIG_I2C_SIS630=y
CONFIG_I2C_SIS96X=y
CONFIG_I2C_VIA=y
CONFIG_I2C_VIAPRO=y

#
# I2C system bus drivers (mostly embedded / system-on-chip)
#
CONFIG_I2C_ALTERA=y
CONFIG_I2C_ASPEED=y
CONFIG_I2C_AT91=y
CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL=y
CONFIG_I2C_AXXIA=y
CONFIG_I2C_BCM2835=y
CONFIG_I2C_BCM_IPROC=y
CONFIG_I2C_BCM_KONA=y
CONFIG_I2C_BRCMSTB=y
CONFIG_I2C_CADENCE=y
CONFIG_I2C_CBUS_GPIO=y
CONFIG_I2C_DAVINCI=y
CONFIG_I2C_DESIGNWARE_CORE=y
CONFIG_I2C_DESIGNWARE_SLAVE=y
CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_I2C_DESIGNWARE_PCI=y
CONFIG_I2C_DIGICOLOR=y
CONFIG_I2C_EG20T=y
CONFIG_I2C_EMEV2=y
CONFIG_I2C_EXYNOS5=y
CONFIG_I2C_GPIO=y
CONFIG_I2C_GPIO_FAULT_INJECTOR=y
CONFIG_I2C_HIGHLANDER=y
CONFIG_I2C_HISI=y
CONFIG_I2C_IMG=y
CONFIG_I2C_IMX=y
CONFIG_I2C_IMX_LPI2C=y
CONFIG_I2C_IOP3XX=y
CONFIG_I2C_JZ4780=y
CONFIG_I2C_KEMPLD=y
CONFIG_I2C_LPC2K=y
CONFIG_I2C_MESON=y
CONFIG_I2C_MICROCHIP_CORE=y
CONFIG_I2C_MT65XX=y
CONFIG_I2C_MT7621=y
CONFIG_I2C_MV64XXX=y
CONFIG_I2C_MXS=y
CONFIG_I2C_NPCM=y
CONFIG_I2C_OCORES=y
CONFIG_I2C_OMAP=y
CONFIG_I2C_OWL=y
CONFIG_I2C_APPLE=y
CONFIG_I2C_PCA_PLATFORM=y
CONFIG_I2C_PNX=y
CONFIG_I2C_PXA=y
CONFIG_I2C_PXA_SLAVE=y
CONFIG_I2C_QCOM_CCI=y
CONFIG_I2C_QCOM_GENI=y
CONFIG_I2C_QUP=y
CONFIG_I2C_RIIC=y
CONFIG_I2C_RK3X=y
CONFIG_I2C_RZV2M=y
CONFIG_I2C_S3C2410=y
CONFIG_I2C_SH_MOBILE=y
CONFIG_I2C_SIMTEC=y
CONFIG_I2C_SPRD=y
CONFIG_I2C_ST=y
CONFIG_I2C_STM32F4=y
CONFIG_I2C_STM32F7=y
CONFIG_I2C_SUN6I_P2WI=y
CONFIG_I2C_SYNQUACER=y
CONFIG_I2C_TEGRA=y
CONFIG_I2C_TEGRA_BPMP=y
CONFIG_I2C_UNIPHIER=y
CONFIG_I2C_UNIPHIER_F=y
CONFIG_I2C_VERSATILE=y
CONFIG_I2C_WMT=y
CONFIG_I2C_THUNDERX=y
CONFIG_I2C_XILINX=y
CONFIG_I2C_XLP9XX=y
CONFIG_I2C_RCAR=y

#
# External I2C/SMBus adapter drivers
#
CONFIG_I2C_DIOLAN_U2C=y
CONFIG_I2C_DLN2=y
CONFIG_I2C_CP2615=y
CONFIG_I2C_PARPORT=y
CONFIG_I2C_PCI1XXXX=y
CONFIG_I2C_ROBOTFUZZ_OSIF=y
CONFIG_I2C_TAOS_EVM=y
CONFIG_I2C_TINY_USB=y
CONFIG_I2C_VIPERBOARD=y

#
# Other I2C/SMBus bus drivers
#
CONFIG_I2C_MLXCPLD=y
CONFIG_I2C_CROS_EC_TUNNEL=y
CONFIG_I2C_FSI=y
CONFIG_I2C_VIRTIO=y
# end of I2C Hardware Bus support

CONFIG_I2C_STUB=m
CONFIG_I2C_SLAVE=y
CONFIG_I2C_SLAVE_EEPROM=y
CONFIG_I2C_SLAVE_TESTUNIT=y
CONFIG_I2C_DEBUG_CORE=y
CONFIG_I2C_DEBUG_ALGO=y
CONFIG_I2C_DEBUG_BUS=y
# end of I2C support

CONFIG_I3C=y
CONFIG_CDNS_I3C_MASTER=y
CONFIG_DW_I3C_MASTER=y
CONFIG_SVC_I3C_MASTER=y
CONFIG_MIPI_I3C_HCI=y
CONFIG_SPI=y
CONFIG_SPI_DEBUG=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y

#
# SPI Master Controller Drivers
#
CONFIG_SPI_ALTERA=y
CONFIG_SPI_ALTERA_CORE=y
CONFIG_SPI_ALTERA_DFL=y
CONFIG_SPI_AR934X=y
CONFIG_SPI_ATH79=y
CONFIG_SPI_ARMADA_3700=y
CONFIG_SPI_ASPEED_SMC=y
CONFIG_SPI_ATMEL=y
CONFIG_SPI_AT91_USART=y
CONFIG_SPI_ATMEL_QUADSPI=y
CONFIG_SPI_AXI_SPI_ENGINE=y
CONFIG_SPI_BCM2835=y
CONFIG_SPI_BCM2835AUX=y
CONFIG_SPI_BCM63XX=y
CONFIG_SPI_BCM63XX_HSSPI=y
CONFIG_SPI_BCM_QSPI=y
CONFIG_SPI_BITBANG=y
CONFIG_SPI_BUTTERFLY=y
CONFIG_SPI_CADENCE=y
CONFIG_SPI_CADENCE_QUADSPI=y
CONFIG_SPI_CADENCE_XSPI=y
CONFIG_SPI_CLPS711X=y
CONFIG_SPI_DESIGNWARE=y
CONFIG_SPI_DW_DMA=y
CONFIG_SPI_DW_PCI=y
CONFIG_SPI_DW_MMIO=y
CONFIG_SPI_DW_BT1=y
CONFIG_SPI_DW_BT1_DIRMAP=y
CONFIG_SPI_DLN2=y
CONFIG_SPI_EP93XX=y
CONFIG_SPI_FSI=y
CONFIG_SPI_FSL_LPSPI=y
CONFIG_SPI_FSL_QUADSPI=y
CONFIG_SPI_GXP=y
CONFIG_SPI_HISI_KUNPENG=y
CONFIG_SPI_HISI_SFC_V3XX=y
CONFIG_SPI_NXP_FLEXSPI=y
CONFIG_SPI_GPIO=y
CONFIG_SPI_IMG_SPFI=y
CONFIG_SPI_IMX=y
CONFIG_SPI_INGENIC=y
CONFIG_SPI_INTEL=y
CONFIG_SPI_INTEL_PCI=y
CONFIG_SPI_INTEL_PLATFORM=y
CONFIG_SPI_JCORE=y
CONFIG_SPI_LM70_LLP=y
CONFIG_SPI_LP8841_RTC=y
CONFIG_SPI_FSL_LIB=y
CONFIG_SPI_FSL_SPI=y
CONFIG_SPI_FSL_DSPI=y
CONFIG_SPI_MESON_SPICC=y
CONFIG_SPI_MESON_SPIFC=y
CONFIG_SPI_MICROCHIP_CORE=y
CONFIG_SPI_MICROCHIP_CORE_QSPI=y
CONFIG_SPI_MT65XX=y
CONFIG_SPI_MT7621=y
CONFIG_SPI_MTK_NOR=y
CONFIG_SPI_MTK_SNFI=y
CONFIG_SPI_NPCM_FIU=y
CONFIG_SPI_NPCM_PSPI=y
CONFIG_SPI_LANTIQ_SSC=y
CONFIG_SPI_OC_TINY=y
CONFIG_SPI_OMAP24XX=y
CONFIG_SPI_TI_QSPI=y
CONFIG_SPI_OMAP_100K=y
CONFIG_SPI_ORION=y
CONFIG_SPI_PIC32=y
CONFIG_SPI_PIC32_SQI=y
CONFIG_SPI_PXA2XX=y
CONFIG_SPI_PXA2XX_PCI=y
CONFIG_SPI_ROCKCHIP=y
CONFIG_SPI_ROCKCHIP_SFC=y
CONFIG_SPI_RPCIF=y
CONFIG_SPI_RSPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_QCOM_GENI=y
CONFIG_SPI_S3C64XX=y
CONFIG_SPI_SC18IS602=y
CONFIG_SPI_SH_MSIOF=y
CONFIG_SPI_SH=y
CONFIG_SPI_SH_HSPI=y
CONFIG_SPI_SIFIVE=y
CONFIG_SPI_SLAVE_MT27XX=y
CONFIG_SPI_SPRD=y
CONFIG_SPI_SPRD_ADI=y
CONFIG_SPI_STM32=y
CONFIG_SPI_STM32_QSPI=y
CONFIG_SPI_ST_SSC4=y
CONFIG_SPI_SUN4I=y
CONFIG_SPI_SUN6I=y
CONFIG_SPI_SUNPLUS_SP7021=y
CONFIG_SPI_SYNQUACER=y
CONFIG_SPI_MXIC=y
CONFIG_SPI_TEGRA210_QUAD=y
CONFIG_SPI_TEGRA114=y
CONFIG_SPI_TEGRA20_SFLASH=y
CONFIG_SPI_TEGRA20_SLINK=y
CONFIG_SPI_THUNDERX=y
CONFIG_SPI_TOPCLIFF_PCH=y
CONFIG_SPI_UNIPHIER=y
CONFIG_SPI_XCOMM=y
CONFIG_SPI_XILINX=y
CONFIG_SPI_XLP=y
CONFIG_SPI_XTENSA_XTFPGA=y
CONFIG_SPI_ZYNQ_QSPI=y
CONFIG_SPI_ZYNQMP_GQSPI=y
CONFIG_SPI_AMD=y

#
# SPI Multiplexer support
#
CONFIG_SPI_MUX=y

#
# SPI Protocol Masters
#
CONFIG_SPI_SPIDEV=y
CONFIG_SPI_LOOPBACK_TEST=m
CONFIG_SPI_TLE62X0=y
CONFIG_SPI_SLAVE=y
CONFIG_SPI_SLAVE_TIME=y
CONFIG_SPI_SLAVE_SYSTEM_CONTROL=y
CONFIG_SPI_DYNAMIC=y
CONFIG_SPMI=y
CONFIG_SPMI_HISI3670=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_SPMI_MTK_PMIF=y
CONFIG_HSI=y
CONFIG_HSI_BOARDINFO=y

#
# HSI controllers
#

#
# HSI clients
#
CONFIG_HSI_CHAR=y
CONFIG_PPS=y
CONFIG_PPS_DEBUG=y

#
# PPS clients support
#
CONFIG_PPS_CLIENT_KTIMER=y
CONFIG_PPS_CLIENT_LDISC=y
CONFIG_PPS_CLIENT_PARPORT=y
CONFIG_PPS_CLIENT_GPIO=y

#
# PPS generators support
#

#
# PTP clock support
#
CONFIG_PTP_1588_CLOCK=y
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
CONFIG_PTP_1588_CLOCK_DTE=y
CONFIG_PTP_1588_CLOCK_QORIQ=y
CONFIG_DP83640_PHY=y
CONFIG_PTP_1588_CLOCK_INES=y
CONFIG_PTP_1588_CLOCK_PCH=y
CONFIG_PTP_1588_CLOCK_IDT82P33=y
CONFIG_PTP_1588_CLOCK_IDTCM=y
CONFIG_PTP_1588_CLOCK_OCP=y
# end of PTP clock support

CONFIG_PINCTRL=y
CONFIG_GENERIC_PINCTRL_GROUPS=y
CONFIG_PINMUX=y
CONFIG_GENERIC_PINMUX_FUNCTIONS=y
CONFIG_PINCONF=y
CONFIG_GENERIC_PINCONF=y
CONFIG_DEBUG_PINCTRL=y
CONFIG_PINCTRL_AMD=y
CONFIG_PINCTRL_AS3722=y
CONFIG_PINCTRL_AT91PIO4=y
CONFIG_PINCTRL_AXP209=y
CONFIG_PINCTRL_BM1880=y
CONFIG_PINCTRL_CY8C95X0=y
CONFIG_PINCTRL_DA850_PUPD=y
CONFIG_PINCTRL_DA9062=y
CONFIG_PINCTRL_EQUILIBRIUM=y
CONFIG_PINCTRL_INGENIC=y
CONFIG_PINCTRL_LPC18XX=y
CONFIG_PINCTRL_MAX77620=y
CONFIG_PINCTRL_MCP23S08_I2C=y
CONFIG_PINCTRL_MCP23S08_SPI=y
CONFIG_PINCTRL_MCP23S08=y
CONFIG_PINCTRL_MICROCHIP_SGPIO=y
CONFIG_PINCTRL_OCELOT=y
CONFIG_PINCTRL_PALMAS=y
CONFIG_PINCTRL_PISTACHIO=y
CONFIG_PINCTRL_RK805=y
CONFIG_PINCTRL_ROCKCHIP=y
CONFIG_PINCTRL_SINGLE=y
CONFIG_PINCTRL_STMFX=y
CONFIG_PINCTRL_SX150X=y
CONFIG_PINCTRL_OWL=y
CONFIG_PINCTRL_S500=y
CONFIG_PINCTRL_S700=y
CONFIG_PINCTRL_S900=y
CONFIG_PINCTRL_ASPEED=y
CONFIG_PINCTRL_ASPEED_G4=y
CONFIG_PINCTRL_ASPEED_G5=y
CONFIG_PINCTRL_ASPEED_G6=y
CONFIG_PINCTRL_BCM281XX=y
CONFIG_PINCTRL_BCM2835=y
CONFIG_PINCTRL_BCM4908=y
CONFIG_PINCTRL_BCM63XX=y
CONFIG_PINCTRL_BCM6318=y
CONFIG_PINCTRL_BCM6328=y
CONFIG_PINCTRL_BCM6358=y
CONFIG_PINCTRL_BCM6362=y
CONFIG_PINCTRL_BCM6368=y
CONFIG_PINCTRL_BCM63268=y
CONFIG_PINCTRL_IPROC_GPIO=y
CONFIG_PINCTRL_CYGNUS_MUX=y
CONFIG_PINCTRL_NS=y
CONFIG_PINCTRL_NSP_GPIO=y
CONFIG_PINCTRL_NS2_MUX=y
CONFIG_PINCTRL_NSP_MUX=y
CONFIG_PINCTRL_BERLIN=y
CONFIG_PINCTRL_AS370=y
CONFIG_PINCTRL_BERLIN_BG4CT=y
CONFIG_PINCTRL_LOCHNAGAR=y
CONFIG_PINCTRL_MADERA=y
CONFIG_PINCTRL_CS47L15=y
CONFIG_PINCTRL_CS47L35=y
CONFIG_PINCTRL_CS47L85=y
CONFIG_PINCTRL_CS47L90=y
CONFIG_PINCTRL_CS47L92=y
CONFIG_PINCTRL_IMX=y
CONFIG_PINCTRL_IMX8MM=y
CONFIG_PINCTRL_IMX8MN=y
CONFIG_PINCTRL_IMX8MP=y
CONFIG_PINCTRL_IMX8MQ=y

#
# Intel pinctrl drivers
#
# end of Intel pinctrl drivers

#
# MediaTek pinctrl drivers
#
CONFIG_EINT_MTK=y
CONFIG_PINCTRL_MTK=y
CONFIG_PINCTRL_MTK_V2=y
CONFIG_PINCTRL_MTK_MOORE=y
CONFIG_PINCTRL_MTK_PARIS=y
CONFIG_PINCTRL_MT2701=y
CONFIG_PINCTRL_MT7623=y
CONFIG_PINCTRL_MT7629=y
CONFIG_PINCTRL_MT8135=y
CONFIG_PINCTRL_MT8127=y
CONFIG_PINCTRL_MT2712=y
CONFIG_PINCTRL_MT6765=y
CONFIG_PINCTRL_MT6779=y
CONFIG_PINCTRL_MT6795=y
CONFIG_PINCTRL_MT6797=y
CONFIG_PINCTRL_MT7622=y
CONFIG_PINCTRL_MT7986=y
CONFIG_PINCTRL_MT8167=y
CONFIG_PINCTRL_MT8173=y
CONFIG_PINCTRL_MT8183=y
CONFIG_PINCTRL_MT8186=y
CONFIG_PINCTRL_MT8188=y
CONFIG_PINCTRL_MT8192=y
CONFIG_PINCTRL_MT8195=y
CONFIG_PINCTRL_MT8365=y
CONFIG_PINCTRL_MT8516=y
CONFIG_PINCTRL_MT6397=y
# end of MediaTek pinctrl drivers

CONFIG_PINCTRL_MESON=y
CONFIG_PINCTRL_WPCM450=y
CONFIG_PINCTRL_NPCM7XX=y
CONFIG_PINCTRL_PXA=y
CONFIG_PINCTRL_PXA25X=y
CONFIG_PINCTRL_PXA27X=y
CONFIG_PINCTRL_MSM=y
CONFIG_PINCTRL_APQ8064=y
CONFIG_PINCTRL_APQ8084=y
CONFIG_PINCTRL_IPQ4019=y
CONFIG_PINCTRL_IPQ8064=y
CONFIG_PINCTRL_IPQ8074=y
CONFIG_PINCTRL_IPQ6018=y
CONFIG_PINCTRL_MSM8226=y
CONFIG_PINCTRL_MSM8660=y
CONFIG_PINCTRL_MSM8960=y
CONFIG_PINCTRL_MDM9607=y
CONFIG_PINCTRL_MDM9615=y
CONFIG_PINCTRL_MSM8X74=y
CONFIG_PINCTRL_MSM8909=y
CONFIG_PINCTRL_MSM8916=y
CONFIG_PINCTRL_MSM8953=y
CONFIG_PINCTRL_MSM8976=y
CONFIG_PINCTRL_MSM8994=y
CONFIG_PINCTRL_MSM8996=y
CONFIG_PINCTRL_MSM8998=y
CONFIG_PINCTRL_QCM2290=y
CONFIG_PINCTRL_QCS404=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_PINCTRL_QCOM_SSBI_PMIC=y
CONFIG_PINCTRL_SC7180=y
CONFIG_PINCTRL_SC7280=y
CONFIG_PINCTRL_SC7280_LPASS_LPI=y
CONFIG_PINCTRL_SC8180X=y
CONFIG_PINCTRL_SC8280XP=y
CONFIG_PINCTRL_SDM660=y
CONFIG_PINCTRL_SDM845=y
CONFIG_PINCTRL_SDX55=y
CONFIG_PINCTRL_SM6115=y
CONFIG_PINCTRL_SM6125=y
CONFIG_PINCTRL_SM6350=y
CONFIG_PINCTRL_SM6375=y
CONFIG_PINCTRL_SDX65=y
CONFIG_PINCTRL_SM8150=y
CONFIG_PINCTRL_SM8250=y
CONFIG_PINCTRL_SM8250_LPASS_LPI=y
CONFIG_PINCTRL_SM8350=y
CONFIG_PINCTRL_SM8450=y
CONFIG_PINCTRL_SM8450_LPASS_LPI=y
CONFIG_PINCTRL_SC8280XP_LPASS_LPI=y
CONFIG_PINCTRL_LPASS_LPI=y

#
# Renesas pinctrl drivers
#
CONFIG_PINCTRL_RENESAS=y
CONFIG_PINCTRL_SH_PFC=y
CONFIG_PINCTRL_SH_PFC_GPIO=y
CONFIG_PINCTRL_SH_FUNC_GPIO=y
CONFIG_PINCTRL_PFC_EMEV2=y
CONFIG_PINCTRL_PFC_R8A77995=y
CONFIG_PINCTRL_PFC_R8A7794=y
CONFIG_PINCTRL_PFC_R8A77990=y
CONFIG_PINCTRL_PFC_R8A7779=y
CONFIG_PINCTRL_PFC_R8A7790=y
CONFIG_PINCTRL_PFC_R8A77950=y
CONFIG_PINCTRL_PFC_R8A77951=y
CONFIG_PINCTRL_PFC_R8A7778=y
CONFIG_PINCTRL_PFC_R8A7793=y
CONFIG_PINCTRL_PFC_R8A7791=y
CONFIG_PINCTRL_PFC_R8A77965=y
CONFIG_PINCTRL_PFC_R8A77960=y
CONFIG_PINCTRL_PFC_R8A77961=y
CONFIG_PINCTRL_PFC_R8A779F0=y
CONFIG_PINCTRL_PFC_R8A7792=y
CONFIG_PINCTRL_PFC_R8A77980=y
CONFIG_PINCTRL_PFC_R8A77970=y
CONFIG_PINCTRL_PFC_R8A779A0=y
CONFIG_PINCTRL_PFC_R8A779G0=y
CONFIG_PINCTRL_PFC_R8A7740=y
CONFIG_PINCTRL_PFC_R8A73A4=y
CONFIG_PINCTRL_RZA1=y
CONFIG_PINCTRL_RZA2=y
CONFIG_PINCTRL_RZG2L=y
CONFIG_PINCTRL_PFC_R8A77470=y
CONFIG_PINCTRL_PFC_R8A7745=y
CONFIG_PINCTRL_PFC_R8A7742=y
CONFIG_PINCTRL_PFC_R8A7743=y
CONFIG_PINCTRL_PFC_R8A7744=y
CONFIG_PINCTRL_PFC_R8A774C0=y
CONFIG_PINCTRL_PFC_R8A774E1=y
CONFIG_PINCTRL_PFC_R8A774A1=y
CONFIG_PINCTRL_PFC_R8A774B1=y
CONFIG_PINCTRL_RZN1=y
CONFIG_PINCTRL_RZV2M=y
CONFIG_PINCTRL_PFC_SH7203=y
CONFIG_PINCTRL_PFC_SH7264=y
CONFIG_PINCTRL_PFC_SH7269=y
CONFIG_PINCTRL_PFC_SH7720=y
CONFIG_PINCTRL_PFC_SH7722=y
CONFIG_PINCTRL_PFC_SH7734=y
CONFIG_PINCTRL_PFC_SH7757=y
CONFIG_PINCTRL_PFC_SH7785=y
CONFIG_PINCTRL_PFC_SH7786=y
CONFIG_PINCTRL_PFC_SH73A0=y
CONFIG_PINCTRL_PFC_SH7723=y
CONFIG_PINCTRL_PFC_SH7724=y
CONFIG_PINCTRL_PFC_SHX3=y
# end of Renesas pinctrl drivers

CONFIG_PINCTRL_SAMSUNG=y
CONFIG_PINCTRL_EXYNOS=y
CONFIG_PINCTRL_EXYNOS_ARM=y
CONFIG_PINCTRL_EXYNOS_ARM64=y
CONFIG_PINCTRL_S3C24XX=y
CONFIG_PINCTRL_S3C64XX=y
CONFIG_PINCTRL_SPRD=y
CONFIG_PINCTRL_SPRD_SC9860=y
CONFIG_PINCTRL_STARFIVE_JH7100=y
CONFIG_PINCTRL_STM32=y
CONFIG_PINCTRL_STM32F429=y
CONFIG_PINCTRL_STM32F469=y
CONFIG_PINCTRL_STM32F746=y
CONFIG_PINCTRL_STM32F769=y
CONFIG_PINCTRL_STM32H743=y
CONFIG_PINCTRL_STM32MP135=y
CONFIG_PINCTRL_STM32MP157=y
CONFIG_PINCTRL_TI_IODELAY=y
CONFIG_PINCTRL_UNIPHIER=y
CONFIG_PINCTRL_UNIPHIER_LD4=y
CONFIG_PINCTRL_UNIPHIER_PRO4=y
CONFIG_PINCTRL_UNIPHIER_SLD8=y
CONFIG_PINCTRL_UNIPHIER_PRO5=y
CONFIG_PINCTRL_UNIPHIER_PXS2=y
CONFIG_PINCTRL_UNIPHIER_LD6B=y
CONFIG_PINCTRL_UNIPHIER_LD11=y
CONFIG_PINCTRL_UNIPHIER_LD20=y
CONFIG_PINCTRL_UNIPHIER_PXS3=y
CONFIG_PINCTRL_UNIPHIER_NX1=y
CONFIG_PINCTRL_VISCONTI=y
CONFIG_PINCTRL_TMPV7700=y
CONFIG_GPIOLIB=y
CONFIG_GPIOLIB_FASTPATH_LIMIT=512
CONFIG_OF_GPIO=y
CONFIG_GPIOLIB_IRQCHIP=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_CDEV=y
CONFIG_GPIO_CDEV_V1=y
CONFIG_GPIO_GENERIC=y
CONFIG_GPIO_REGMAP=y
CONFIG_GPIO_MAX730X=y

#
# Memory mapped GPIO drivers
#
CONFIG_GPIO_74XX_MMIO=y
CONFIG_GPIO_ALTERA=y
CONFIG_GPIO_ASPEED=y
CONFIG_GPIO_ASPEED_SGPIO=y
CONFIG_GPIO_ATH79=y
CONFIG_GPIO_RASPBERRYPI_EXP=y
CONFIG_GPIO_BCM_KONA=y
CONFIG_GPIO_BCM_XGS_IPROC=y
CONFIG_GPIO_BRCMSTB=y
CONFIG_GPIO_CADENCE=y
CONFIG_GPIO_CLPS711X=y
CONFIG_GPIO_DWAPB=y
CONFIG_GPIO_EIC_SPRD=y
CONFIG_GPIO_EM=y
CONFIG_GPIO_EXAR=y
CONFIG_GPIO_FTGPIO010=y
CONFIG_GPIO_GENERIC_PLATFORM=y
CONFIG_GPIO_GRGPIO=y
CONFIG_GPIO_HISI=y
CONFIG_GPIO_HLWD=y
CONFIG_GPIO_IMX_SCU=y
CONFIG_GPIO_IOP=y
CONFIG_GPIO_LOGICVC=y
CONFIG_GPIO_LPC18XX=y
CONFIG_GPIO_LPC32XX=y
CONFIG_GPIO_MB86S7X=y
CONFIG_GPIO_MENZ127=y
CONFIG_GPIO_MPC8XXX=y
CONFIG_GPIO_MT7621=y
CONFIG_GPIO_MXC=y
CONFIG_GPIO_MXS=y
CONFIG_GPIO_PMIC_EIC_SPRD=y
CONFIG_GPIO_PXA=y
CONFIG_GPIO_RCAR=y
CONFIG_GPIO_RDA=y
CONFIG_GPIO_ROCKCHIP=y
CONFIG_GPIO_SAMA5D2_PIOBU=y
CONFIG_GPIO_SIFIVE=y
CONFIG_GPIO_SIOX=y
CONFIG_GPIO_SNPS_CREG=y
CONFIG_GPIO_SPRD=y
CONFIG_GPIO_STP_XWAY=y
CONFIG_GPIO_SYSCON=y
CONFIG_GPIO_TEGRA=y
CONFIG_GPIO_TEGRA186=y
CONFIG_GPIO_TS4800=y
CONFIG_GPIO_THUNDERX=y
CONFIG_GPIO_UNIPHIER=y
CONFIG_GPIO_VISCONTI=y
CONFIG_GPIO_VX855=y
CONFIG_GPIO_WCD934X=y
CONFIG_GPIO_XGENE_SB=y
CONFIG_GPIO_XILINX=y
CONFIG_GPIO_XLP=y
CONFIG_GPIO_AMD_FCH=y
CONFIG_GPIO_IDT3243X=y
# end of Memory mapped GPIO drivers

#
# I2C GPIO expanders
#
CONFIG_GPIO_ADNP=y
CONFIG_GPIO_GW_PLD=y
CONFIG_GPIO_MAX7300=y
CONFIG_GPIO_MAX732X=y
CONFIG_GPIO_MAX732X_IRQ=y
CONFIG_GPIO_PCA953X=y
CONFIG_GPIO_PCA953X_IRQ=y
CONFIG_GPIO_PCA9570=y
CONFIG_GPIO_PCF857X=y
CONFIG_GPIO_TPIC2810=y
CONFIG_GPIO_TS4900=y
# end of I2C GPIO expanders

#
# MFD GPIO expanders
#
CONFIG_GPIO_ADP5520=y
CONFIG_GPIO_ARIZONA=y
CONFIG_GPIO_BD71815=y
CONFIG_GPIO_BD71828=y
CONFIG_GPIO_BD9571MWV=y
CONFIG_GPIO_CRYSTAL_COVE=y
CONFIG_GPIO_DA9052=y
CONFIG_GPIO_DA9055=y
CONFIG_GPIO_DLN2=y
CONFIG_GPIO_JANZ_TTL=y
CONFIG_GPIO_KEMPLD=y
CONFIG_GPIO_LP3943=y
CONFIG_GPIO_LP873X=y
CONFIG_GPIO_LP87565=y
CONFIG_GPIO_MADERA=y
CONFIG_GPIO_MAX77620=y
CONFIG_GPIO_MAX77650=y
CONFIG_GPIO_PALMAS=y
CONFIG_GPIO_RC5T583=y
CONFIG_GPIO_SL28CPLD=y
CONFIG_GPIO_STMPE=y
CONFIG_GPIO_TC3589X=y
CONFIG_GPIO_TIMBERDALE=y
CONFIG_GPIO_TPS65086=y
CONFIG_GPIO_TPS65218=y
CONFIG_GPIO_TPS6586X=y
CONFIG_GPIO_TPS65910=y
CONFIG_GPIO_TPS65912=y
CONFIG_GPIO_TQMX86=y
CONFIG_GPIO_TWL4030=y
CONFIG_GPIO_TWL6040=y
CONFIG_GPIO_UCB1400=y
CONFIG_GPIO_WM831X=y
CONFIG_GPIO_WM8350=y
CONFIG_GPIO_WM8994=y
# end of MFD GPIO expanders

#
# PCI GPIO expanders
#
CONFIG_GPIO_AMD8111=y
CONFIG_GPIO_MLXBF=y
CONFIG_GPIO_MLXBF2=y
CONFIG_GPIO_ML_IOH=y
CONFIG_GPIO_PCH=y
CONFIG_GPIO_PCI_IDIO_16=y
CONFIG_GPIO_PCIE_IDIO_24=y
CONFIG_GPIO_RDC321X=y
# end of PCI GPIO expanders

#
# SPI GPIO expanders
#
CONFIG_GPIO_74X164=y
CONFIG_GPIO_MAX3191X=y
CONFIG_GPIO_MAX7301=y
CONFIG_GPIO_MC33880=y
CONFIG_GPIO_PISOSR=y
CONFIG_GPIO_XRA1403=y
CONFIG_GPIO_MOXTET=y
# end of SPI GPIO expanders

#
# USB GPIO expanders
#
CONFIG_GPIO_VIPERBOARD=y
# end of USB GPIO expanders

#
# Virtual GPIO drivers
#
CONFIG_GPIO_AGGREGATOR=y
CONFIG_GPIO_MOCKUP=y
CONFIG_GPIO_VIRTIO=y
CONFIG_GPIO_SIM=y
# end of Virtual GPIO drivers

CONFIG_W1=y
CONFIG_W1_CON=y

#
# 1-wire Bus Masters
#
CONFIG_W1_MASTER_MATROX=y
CONFIG_W1_MASTER_DS2490=y
CONFIG_W1_MASTER_DS2482=y
CONFIG_W1_MASTER_MXC=y
CONFIG_W1_MASTER_DS1WM=y
CONFIG_W1_MASTER_GPIO=y
CONFIG_W1_MASTER_SGI=y
# end of 1-wire Bus Masters

#
# 1-wire Slaves
#
CONFIG_W1_SLAVE_THERM=y
CONFIG_W1_SLAVE_SMEM=y
CONFIG_W1_SLAVE_DS2405=y
CONFIG_W1_SLAVE_DS2408=y
CONFIG_W1_SLAVE_DS2408_READBACK=y
CONFIG_W1_SLAVE_DS2413=y
CONFIG_W1_SLAVE_DS2406=y
CONFIG_W1_SLAVE_DS2423=y
CONFIG_W1_SLAVE_DS2805=y
CONFIG_W1_SLAVE_DS2430=y
CONFIG_W1_SLAVE_DS2431=y
CONFIG_W1_SLAVE_DS2433=y
CONFIG_W1_SLAVE_DS2433_CRC=y
CONFIG_W1_SLAVE_DS2438=y
CONFIG_W1_SLAVE_DS250X=y
CONFIG_W1_SLAVE_DS2780=y
CONFIG_W1_SLAVE_DS2781=y
CONFIG_W1_SLAVE_DS28E04=y
CONFIG_W1_SLAVE_DS28E17=y
# end of 1-wire Slaves

CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_AS3722=y
CONFIG_POWER_RESET_ATC260X=y
CONFIG_POWER_RESET_BRCMKONA=y
CONFIG_POWER_RESET_BRCMSTB=y
CONFIG_POWER_RESET_GEMINI_POWEROFF=y
CONFIG_POWER_RESET_GPIO=y
CONFIG_POWER_RESET_GPIO_RESTART=y
CONFIG_POWER_RESET_LINKSTATION=y
CONFIG_POWER_RESET_OCELOT_RESET=y
CONFIG_POWER_RESET_PIIX4_POWEROFF=y
CONFIG_POWER_RESET_LTC2952=y
CONFIG_POWER_RESET_MT6323=y
CONFIG_POWER_RESET_REGULATOR=y
CONFIG_POWER_RESET_RESTART=y
CONFIG_POWER_RESET_TPS65086=y
CONFIG_POWER_RESET_KEYSTONE=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_POWER_RESET_SYSCON_POWEROFF=y
CONFIG_POWER_RESET_RMOBILE=y
CONFIG_REBOOT_MODE=y
CONFIG_SYSCON_REBOOT_MODE=y
CONFIG_POWER_RESET_SC27XX=y
CONFIG_NVMEM_REBOOT_MODE=y
CONFIG_POWER_SUPPLY=y
CONFIG_POWER_SUPPLY_DEBUG=y
CONFIG_POWER_SUPPLY_HWMON=y
CONFIG_PDA_POWER=y
CONFIG_GENERIC_ADC_BATTERY=y
CONFIG_IP5XXX_POWER=y
CONFIG_MAX8925_POWER=y
CONFIG_WM831X_BACKUP=y
CONFIG_WM831X_POWER=y
CONFIG_WM8350_POWER=y
CONFIG_TEST_POWER=y
CONFIG_BATTERY_88PM860X=y
CONFIG_CHARGER_ADP5061=y
CONFIG_BATTERY_ACT8945A=y
CONFIG_BATTERY_CPCAP=y
CONFIG_BATTERY_CW2015=y
CONFIG_BATTERY_DS2760=y
CONFIG_BATTERY_DS2780=y
CONFIG_BATTERY_DS2781=y
CONFIG_BATTERY_DS2782=y
CONFIG_BATTERY_LEGO_EV3=y
CONFIG_BATTERY_OLPC=y
CONFIG_BATTERY_SAMSUNG_SDI=y
CONFIG_BATTERY_INGENIC=y
CONFIG_BATTERY_WM97XX=y
CONFIG_BATTERY_SBS=y
CONFIG_CHARGER_SBS=y
CONFIG_MANAGER_SBS=y
CONFIG_BATTERY_BQ27XXX=y
CONFIG_BATTERY_BQ27XXX_I2C=y
CONFIG_BATTERY_BQ27XXX_HDQ=y
CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM=y
CONFIG_BATTERY_DA9030=y
CONFIG_BATTERY_DA9052=y
CONFIG_CHARGER_DA9150=y
CONFIG_BATTERY_DA9150=y
CONFIG_CHARGER_AXP20X=y
CONFIG_BATTERY_AXP20X=y
CONFIG_AXP20X_POWER=y
CONFIG_BATTERY_MAX17040=y
CONFIG_BATTERY_MAX17042=y
CONFIG_BATTERY_MAX1721X=y
CONFIG_BATTERY_TWL4030_MADC=y
CONFIG_CHARGER_88PM860X=y
CONFIG_CHARGER_PCF50633=y
CONFIG_BATTERY_RX51=y
CONFIG_CHARGER_CPCAP=y
CONFIG_CHARGER_ISP1704=y
CONFIG_CHARGER_MAX8903=y
CONFIG_CHARGER_TWL4030=y
CONFIG_CHARGER_LP8727=y
CONFIG_CHARGER_LP8788=y
CONFIG_CHARGER_GPIO=y
CONFIG_CHARGER_MANAGER=y
CONFIG_CHARGER_LT3651=y
CONFIG_CHARGER_LTC4162L=y
CONFIG_CHARGER_MAX14577=y
CONFIG_CHARGER_DETECTOR_MAX14656=y
CONFIG_CHARGER_MAX77650=y
CONFIG_CHARGER_MAX77693=y
CONFIG_CHARGER_MAX77976=y
CONFIG_CHARGER_MAX8997=y
CONFIG_CHARGER_MAX8998=y
CONFIG_CHARGER_MP2629=y
CONFIG_CHARGER_MT6360=y
CONFIG_CHARGER_MT6370=y
CONFIG_CHARGER_QCOM_SMBB=y
CONFIG_CHARGER_BQ2415X=y
CONFIG_CHARGER_BQ24190=y
CONFIG_CHARGER_BQ24257=y
CONFIG_CHARGER_BQ24735=y
CONFIG_CHARGER_BQ2515X=y
CONFIG_CHARGER_BQ25890=y
CONFIG_CHARGER_BQ25980=y
CONFIG_CHARGER_BQ256XX=y
CONFIG_CHARGER_RK817=y
CONFIG_CHARGER_SMB347=y
CONFIG_CHARGER_TPS65090=y
CONFIG_CHARGER_TPS65217=y
CONFIG_BATTERY_GAUGE_LTC2941=y
CONFIG_BATTERY_GOLDFISH=y
CONFIG_BATTERY_RT5033=y
CONFIG_CHARGER_RT9455=y
CONFIG_CHARGER_CROS_USBPD=y
CONFIG_CHARGER_CROS_PCHG=y
CONFIG_CHARGER_SC2731=y
CONFIG_FUEL_GAUGE_SC27XX=y
CONFIG_CHARGER_UCS1002=y
CONFIG_CHARGER_BD99954=y
CONFIG_RN5T618_POWER=y
CONFIG_BATTERY_ACER_A500=y
CONFIG_BATTERY_UG3105=y
CONFIG_HWMON=y
CONFIG_HWMON_VID=y
CONFIG_HWMON_DEBUG_CHIP=y

#
# Native drivers
#
CONFIG_SENSORS_AD7314=y
CONFIG_SENSORS_AD7414=y
CONFIG_SENSORS_AD7418=y
CONFIG_SENSORS_ADM1025=y
CONFIG_SENSORS_ADM1026=y
CONFIG_SENSORS_ADM1029=y
CONFIG_SENSORS_ADM1031=y
CONFIG_SENSORS_ADM1177=y
CONFIG_SENSORS_ADM9240=y
CONFIG_SENSORS_ADT7X10=y
CONFIG_SENSORS_ADT7310=y
CONFIG_SENSORS_ADT7410=y
CONFIG_SENSORS_ADT7411=y
CONFIG_SENSORS_ADT7462=y
CONFIG_SENSORS_ADT7470=y
CONFIG_SENSORS_ADT7475=y
CONFIG_SENSORS_AHT10=y
CONFIG_SENSORS_AQUACOMPUTER_D5NEXT=y
CONFIG_SENSORS_AS370=y
CONFIG_SENSORS_ASC7621=y
CONFIG_SENSORS_AXI_FAN_CONTROL=y
CONFIG_SENSORS_ARM_SCMI=y
CONFIG_SENSORS_ARM_SCPI=y
CONFIG_SENSORS_ASB100=y
CONFIG_SENSORS_ASPEED=y
CONFIG_SENSORS_ATXP1=y
CONFIG_SENSORS_BT1_PVT=y
CONFIG_SENSORS_BT1_PVT_ALARMS=y
CONFIG_SENSORS_CORSAIR_CPRO=y
CONFIG_SENSORS_CORSAIR_PSU=y
CONFIG_SENSORS_DRIVETEMP=y
CONFIG_SENSORS_DS620=y
CONFIG_SENSORS_DS1621=y
CONFIG_SENSORS_DA9052_ADC=y
CONFIG_SENSORS_DA9055=y
CONFIG_SENSORS_I5K_AMB=y
CONFIG_SENSORS_SPARX5=y
CONFIG_SENSORS_F71805F=y
CONFIG_SENSORS_F71882FG=y
CONFIG_SENSORS_F75375S=y
CONFIG_SENSORS_GSC=y
CONFIG_SENSORS_MC13783_ADC=y
CONFIG_SENSORS_FSCHMD=y
CONFIG_SENSORS_FTSTEUTATES=y
CONFIG_SENSORS_GL518SM=y
CONFIG_SENSORS_GL520SM=y
CONFIG_SENSORS_G760A=y
CONFIG_SENSORS_G762=y
CONFIG_SENSORS_GPIO_FAN=y
CONFIG_SENSORS_HIH6130=y
CONFIG_SENSORS_IBMAEM=y
CONFIG_SENSORS_IBMPEX=y
CONFIG_SENSORS_IIO_HWMON=y
CONFIG_SENSORS_IT87=y
CONFIG_SENSORS_JC42=y
CONFIG_SENSORS_POWR1220=y
CONFIG_SENSORS_LAN966X=y
CONFIG_SENSORS_LINEAGE=y
CONFIG_SENSORS_LOCHNAGAR=y
CONFIG_SENSORS_LTC2945=y
CONFIG_SENSORS_LTC2947=y
CONFIG_SENSORS_LTC2947_I2C=y
CONFIG_SENSORS_LTC2947_SPI=y
CONFIG_SENSORS_LTC2990=y
CONFIG_SENSORS_LTC2992=y
CONFIG_SENSORS_LTC4151=y
CONFIG_SENSORS_LTC4215=y
CONFIG_SENSORS_LTC4222=y
CONFIG_SENSORS_LTC4245=y
CONFIG_SENSORS_LTC4260=y
CONFIG_SENSORS_LTC4261=y
CONFIG_SENSORS_MAX1111=y
CONFIG_SENSORS_MAX127=y
CONFIG_SENSORS_MAX16065=y
CONFIG_SENSORS_MAX1619=y
CONFIG_SENSORS_MAX1668=y
CONFIG_SENSORS_MAX197=y
CONFIG_SENSORS_MAX31722=y
CONFIG_SENSORS_MAX31730=y
CONFIG_SENSORS_MAX31760=y
CONFIG_SENSORS_MAX6620=y
CONFIG_SENSORS_MAX6621=y
CONFIG_SENSORS_MAX6639=y
CONFIG_SENSORS_MAX6650=y
CONFIG_SENSORS_MAX6697=y
CONFIG_SENSORS_MAX31790=y
CONFIG_SENSORS_MCP3021=y
CONFIG_SENSORS_MLXREG_FAN=y
CONFIG_SENSORS_TC654=y
CONFIG_SENSORS_TPS23861=y
CONFIG_SENSORS_MENF21BMC_HWMON=y
CONFIG_SENSORS_MR75203=y
CONFIG_SENSORS_ADCXX=y
CONFIG_SENSORS_LM63=y
CONFIG_SENSORS_LM70=y
CONFIG_SENSORS_LM73=y
CONFIG_SENSORS_LM75=y
CONFIG_SENSORS_LM77=y
CONFIG_SENSORS_LM78=y
CONFIG_SENSORS_LM80=y
CONFIG_SENSORS_LM83=y
CONFIG_SENSORS_LM85=y
CONFIG_SENSORS_LM87=y
CONFIG_SENSORS_LM90=y
CONFIG_SENSORS_LM92=y
CONFIG_SENSORS_LM93=y
CONFIG_SENSORS_LM95234=y
CONFIG_SENSORS_LM95241=y
CONFIG_SENSORS_LM95245=y
CONFIG_SENSORS_PC87360=y
CONFIG_SENSORS_PC87427=y
CONFIG_SENSORS_NTC_THERMISTOR=y
CONFIG_SENSORS_NCT6683=y
CONFIG_SENSORS_NCT6775_CORE=y
CONFIG_SENSORS_NCT6775=y
CONFIG_SENSORS_NCT6775_I2C=y
CONFIG_SENSORS_NCT7802=y
CONFIG_SENSORS_NCT7904=y
CONFIG_SENSORS_NPCM7XX=y
CONFIG_SENSORS_NSA320=y
CONFIG_SENSORS_NZXT_KRAKEN2=y
CONFIG_SENSORS_NZXT_SMART2=y
CONFIG_SENSORS_OCC_P8_I2C=y
CONFIG_SENSORS_OCC=y
CONFIG_SENSORS_PCF8591=y
CONFIG_SENSORS_PECI_CPUTEMP=y
CONFIG_SENSORS_PECI_DIMMTEMP=y
CONFIG_SENSORS_PECI=y
CONFIG_PMBUS=y
CONFIG_SENSORS_PMBUS=y
CONFIG_SENSORS_ADM1266=y
CONFIG_SENSORS_ADM1275=y
CONFIG_SENSORS_BEL_PFE=y
CONFIG_SENSORS_BPA_RS600=y
CONFIG_SENSORS_DELTA_AHE50DC_FAN=y
CONFIG_SENSORS_FSP_3Y=y
CONFIG_SENSORS_IBM_CFFPS=y
CONFIG_SENSORS_DPS920AB=y
CONFIG_SENSORS_INSPUR_IPSPS=y
CONFIG_SENSORS_IR35221=y
CONFIG_SENSORS_IR36021=y
CONFIG_SENSORS_IR38064=y
CONFIG_SENSORS_IR38064_REGULATOR=y
CONFIG_SENSORS_IRPS5401=y
CONFIG_SENSORS_ISL68137=y
CONFIG_SENSORS_LM25066=y
CONFIG_SENSORS_LM25066_REGULATOR=y
CONFIG_SENSORS_LT7182S=y
CONFIG_SENSORS_LTC2978=y
CONFIG_SENSORS_LTC2978_REGULATOR=y
CONFIG_SENSORS_LTC3815=y
CONFIG_SENSORS_MAX15301=y
CONFIG_SENSORS_MAX16064=y
CONFIG_SENSORS_MAX16601=y
CONFIG_SENSORS_MAX20730=y
CONFIG_SENSORS_MAX20751=y
CONFIG_SENSORS_MAX31785=y
CONFIG_SENSORS_MAX34440=y
CONFIG_SENSORS_MAX8688=y
CONFIG_SENSORS_MP2888=y
CONFIG_SENSORS_MP2975=y
CONFIG_SENSORS_MP5023=y
CONFIG_SENSORS_PIM4328=y
CONFIG_SENSORS_PLI1209BC=y
CONFIG_SENSORS_PLI1209BC_REGULATOR=y
CONFIG_SENSORS_PM6764TR=y
CONFIG_SENSORS_PXE1610=y
CONFIG_SENSORS_Q54SJ108A2=y
CONFIG_SENSORS_STPDDC60=y
CONFIG_SENSORS_TPS40422=y
CONFIG_SENSORS_TPS53679=y
CONFIG_SENSORS_TPS546D24=y
CONFIG_SENSORS_UCD9000=y
CONFIG_SENSORS_UCD9200=y
CONFIG_SENSORS_XDPE152=y
CONFIG_SENSORS_XDPE122=y
CONFIG_SENSORS_XDPE122_REGULATOR=y
CONFIG_SENSORS_ZL6100=y
CONFIG_SENSORS_PWM_FAN=y
CONFIG_SENSORS_RASPBERRYPI_HWMON=y
CONFIG_SENSORS_SL28CPLD=y
CONFIG_SENSORS_SBTSI=y
CONFIG_SENSORS_SBRMI=y
CONFIG_SENSORS_SHT15=y
CONFIG_SENSORS_SHT21=y
CONFIG_SENSORS_SHT3x=y
CONFIG_SENSORS_SHT4x=y
CONFIG_SENSORS_SHTC1=y
CONFIG_SENSORS_SIS5595=y
CONFIG_SENSORS_SY7636A=y
CONFIG_SENSORS_DME1737=y
CONFIG_SENSORS_EMC1403=y
CONFIG_SENSORS_EMC2103=y
CONFIG_SENSORS_EMC2305=y
CONFIG_SENSORS_EMC6W201=y
CONFIG_SENSORS_SMSC47M1=y
CONFIG_SENSORS_SMSC47M192=y
CONFIG_SENSORS_SMSC47B397=y
CONFIG_SENSORS_SCH56XX_COMMON=y
CONFIG_SENSORS_SCH5627=y
CONFIG_SENSORS_SCH5636=y
CONFIG_SENSORS_STTS751=y
CONFIG_SENSORS_SMM665=y
CONFIG_SENSORS_ADC128D818=y
CONFIG_SENSORS_ADS7828=y
CONFIG_SENSORS_ADS7871=y
CONFIG_SENSORS_AMC6821=y
CONFIG_SENSORS_INA209=y
CONFIG_SENSORS_INA2XX=y
CONFIG_SENSORS_INA238=y
CONFIG_SENSORS_INA3221=y
CONFIG_SENSORS_TC74=y
CONFIG_SENSORS_THMC50=y
CONFIG_SENSORS_TMP102=y
CONFIG_SENSORS_TMP103=y
CONFIG_SENSORS_TMP108=y
CONFIG_SENSORS_TMP401=y
CONFIG_SENSORS_TMP421=y
CONFIG_SENSORS_TMP464=y
CONFIG_SENSORS_TMP513=y
CONFIG_SENSORS_VIA686A=y
CONFIG_SENSORS_VT1211=y
CONFIG_SENSORS_VT8231=y
CONFIG_SENSORS_W83773G=y
CONFIG_SENSORS_W83781D=y
CONFIG_SENSORS_W83791D=y
CONFIG_SENSORS_W83792D=y
CONFIG_SENSORS_W83793=y
CONFIG_SENSORS_W83795=y
CONFIG_SENSORS_W83795_FANCTRL=y
CONFIG_SENSORS_W83L785TS=y
CONFIG_SENSORS_W83L786NG=y
CONFIG_SENSORS_W83627HF=y
CONFIG_SENSORS_W83627EHF=y
CONFIG_SENSORS_WM831X=y
CONFIG_SENSORS_WM8350=y
CONFIG_SENSORS_ULTRA45=y
CONFIG_SENSORS_INTEL_M10_BMC_HWMON=y
CONFIG_THERMAL=y
CONFIG_THERMAL_NETLINK=y
CONFIG_THERMAL_STATISTICS=y
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
CONFIG_THERMAL_HWMON=y
CONFIG_THERMAL_OF=y
CONFIG_THERMAL_WRITABLE_TRIPS=y
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set
CONFIG_THERMAL_GOV_FAIR_SHARE=y
CONFIG_THERMAL_GOV_STEP_WISE=y
CONFIG_THERMAL_GOV_BANG_BANG=y
CONFIG_THERMAL_GOV_USER_SPACE=y
CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y
CONFIG_CPU_THERMAL=y
CONFIG_CPU_FREQ_THERMAL=y
CONFIG_DEVFREQ_THERMAL=y
CONFIG_THERMAL_EMULATION=y
CONFIG_THERMAL_MMIO=y
CONFIG_HISI_THERMAL=y
CONFIG_IMX_THERMAL=y
CONFIG_IMX_SC_THERMAL=y
CONFIG_IMX8MM_THERMAL=y
CONFIG_K3_THERMAL=y
CONFIG_MAX77620_THERMAL=y
CONFIG_QORIQ_THERMAL=y
CONFIG_SPEAR_THERMAL=y
CONFIG_SUN8I_THERMAL=y
CONFIG_ROCKCHIP_THERMAL=y
CONFIG_RCAR_THERMAL=y
CONFIG_RCAR_GEN3_THERMAL=y
CONFIG_RZG2L_THERMAL=y
CONFIG_KIRKWOOD_THERMAL=y
CONFIG_DOVE_THERMAL=y
CONFIG_ARMADA_THERMAL=y
CONFIG_DA9062_THERMAL=y
CONFIG_MTK_THERMAL=y

#
# Intel thermal drivers
#

#
# ACPI INT340X thermal drivers
#
# end of ACPI INT340X thermal drivers
# end of Intel thermal drivers

#
# Broadcom thermal drivers
#
CONFIG_BCM2711_THERMAL=y
CONFIG_BCM2835_THERMAL=y
CONFIG_BRCMSTB_THERMAL=y
CONFIG_BCM_NS_THERMAL=y
CONFIG_BCM_SR_THERMAL=y
# end of Broadcom thermal drivers

#
# Texas Instruments thermal drivers
#
CONFIG_TI_SOC_THERMAL=y
CONFIG_TI_THERMAL=y
CONFIG_OMAP3_THERMAL=y
CONFIG_OMAP4_THERMAL=y
CONFIG_OMAP5_THERMAL=y
CONFIG_DRA752_THERMAL=y
# end of Texas Instruments thermal drivers

#
# Samsung thermal drivers
#
CONFIG_EXYNOS_THERMAL=y
# end of Samsung thermal drivers

#
# NVIDIA Tegra thermal drivers
#
CONFIG_TEGRA_SOCTHERM=y
CONFIG_TEGRA_BPMP_THERMAL=y
CONFIG_TEGRA30_TSENSOR=y
# end of NVIDIA Tegra thermal drivers

CONFIG_GENERIC_ADC_THERMAL=y

#
# Qualcomm thermal drivers
#
CONFIG_QCOM_TSENS=y
CONFIG_QCOM_SPMI_ADC_TM5=y
CONFIG_QCOM_SPMI_TEMP_ALARM=y
# end of Qualcomm thermal drivers

CONFIG_UNIPHIER_THERMAL=y
CONFIG_SPRD_THERMAL=y
CONFIG_KHADAS_MCU_FAN_THERMAL=y
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_CORE=y
CONFIG_WATCHDOG_NOWAYOUT=y
CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
CONFIG_WATCHDOG_OPEN_TIMEOUT=0
CONFIG_WATCHDOG_SYSFS=y
CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT=y

#
# Watchdog Pretimeout Governors
#
CONFIG_WATCHDOG_PRETIMEOUT_GOV=y
CONFIG_WATCHDOG_PRETIMEOUT_GOV_SEL=m
CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP=y
CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC=y
# CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP is not set
CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC=y

#
# Watchdog Device Drivers
#
CONFIG_SOFT_WATCHDOG=y
CONFIG_SOFT_WATCHDOG_PRETIMEOUT=y
CONFIG_BD957XMUF_WATCHDOG=y
CONFIG_DA9052_WATCHDOG=y
CONFIG_DA9055_WATCHDOG=y
CONFIG_DA9063_WATCHDOG=y
CONFIG_DA9062_WATCHDOG=y
CONFIG_GPIO_WATCHDOG=y
CONFIG_GPIO_WATCHDOG_ARCH_INITCALL=y
CONFIG_MENF21BMC_WATCHDOG=y
CONFIG_MENZ069_WATCHDOG=y
CONFIG_WM831X_WATCHDOG=y
CONFIG_WM8350_WATCHDOG=y
CONFIG_XILINX_WATCHDOG=y
CONFIG_ZIIRAVE_WATCHDOG=y
CONFIG_RAVE_SP_WATCHDOG=y
CONFIG_MLX_WDT=y
CONFIG_SL28CPLD_WATCHDOG=y
CONFIG_ARMADA_37XX_WATCHDOG=y
CONFIG_ASM9260_WATCHDOG=y
CONFIG_AT91RM9200_WATCHDOG=y
CONFIG_AT91SAM9X_WATCHDOG=y
CONFIG_SAMA5D4_WATCHDOG=y
CONFIG_CADENCE_WATCHDOG=y
CONFIG_FTWDT010_WATCHDOG=y
CONFIG_S3C2410_WATCHDOG=y
CONFIG_DW_WATCHDOG=y
CONFIG_EP93XX_WATCHDOG=y
CONFIG_OMAP_WATCHDOG=y
CONFIG_PNX4008_WATCHDOG=y
CONFIG_DAVINCI_WATCHDOG=y
CONFIG_K3_RTI_WATCHDOG=y
CONFIG_RN5T618_WATCHDOG=y
CONFIG_SUNXI_WATCHDOG=y
CONFIG_NPCM7XX_WATCHDOG=y
CONFIG_TWL4030_WATCHDOG=y
CONFIG_STMP3XXX_RTC_WATCHDOG=y
CONFIG_TS4800_WATCHDOG=y
CONFIG_TS72XX_WATCHDOG=y
CONFIG_MAX63XX_WATCHDOG=y
CONFIG_MAX77620_WATCHDOG=y
CONFIG_IMX2_WDT=y
CONFIG_IMX7ULP_WDT=y
CONFIG_RETU_WATCHDOG=y
CONFIG_MOXART_WDT=y
CONFIG_ST_LPC_WATCHDOG=y
CONFIG_TEGRA_WATCHDOG=y
CONFIG_QCOM_WDT=y
CONFIG_MESON_GXBB_WATCHDOG=y
CONFIG_MESON_WATCHDOG=y
CONFIG_MEDIATEK_WATCHDOG=y
CONFIG_DIGICOLOR_WATCHDOG=y
CONFIG_LPC18XX_WATCHDOG=y
CONFIG_RENESAS_WDT=y
CONFIG_RENESAS_RZAWDT=y
CONFIG_RENESAS_RZN1WDT=y
CONFIG_RENESAS_RZG2LWDT=y
CONFIG_ASPEED_WATCHDOG=y
CONFIG_STPMIC1_WATCHDOG=y
CONFIG_UNIPHIER_WATCHDOG=y
CONFIG_RTD119X_WATCHDOG=y
CONFIG_REALTEK_OTTO_WDT=y
CONFIG_SPRD_WATCHDOG=y
CONFIG_PM8916_WATCHDOG=y
CONFIG_VISCONTI_WATCHDOG=y
CONFIG_MSC313E_WATCHDOG=y
CONFIG_APPLE_WATCHDOG=y
CONFIG_SUNPLUS_WATCHDOG=y
CONFIG_ALIM7101_WDT=y
CONFIG_SC520_WDT=y
CONFIG_I6300ESB_WDT=y
CONFIG_KEMPLD_WDT=y
CONFIG_RDC321X_WDT=y
CONFIG_BCM47XX_WDT=y
CONFIG_BCM2835_WDT=y
CONFIG_BCM_KONA_WDT=y
CONFIG_BCM_KONA_WDT_DEBUG=y
CONFIG_BCM7038_WDT=y
CONFIG_IMGPDC_WDT=y
CONFIG_MPC5200_WDT=y
CONFIG_MEN_A21_WDT=y
CONFIG_WATCHDOG_CP1XXX=y
CONFIG_WATCHDOG_RIO=y
CONFIG_WATCHDOG_SUN4V=y
CONFIG_UML_WATCHDOG=y

#
# PCI-based Watchdog Cards
#
CONFIG_PCIPCWATCHDOG=y
CONFIG_WDTPCI=y

#
# USB-based Watchdog Cards
#
CONFIG_USBPCWATCHDOG=y
CONFIG_SSB_POSSIBLE=y
CONFIG_SSB=y
CONFIG_SSB_SPROM=y
CONFIG_SSB_BLOCKIO=y
CONFIG_SSB_PCIHOST_POSSIBLE=y
CONFIG_SSB_PCIHOST=y
CONFIG_SSB_B43_PCI_BRIDGE=y
CONFIG_SSB_PCMCIAHOST_POSSIBLE=y
CONFIG_SSB_PCMCIAHOST=y
CONFIG_SSB_SDIOHOST_POSSIBLE=y
CONFIG_SSB_SDIOHOST=y
CONFIG_SSB_HOST_SOC=y
CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y
CONFIG_SSB_DRIVER_PCICORE=y
CONFIG_SSB_DRIVER_GPIO=y
CONFIG_BCMA_POSSIBLE=y
CONFIG_BCMA=y
CONFIG_BCMA_BLOCKIO=y
CONFIG_BCMA_HOST_PCI_POSSIBLE=y
CONFIG_BCMA_HOST_PCI=y
CONFIG_BCMA_HOST_SOC=y
CONFIG_BCMA_DRIVER_PCI=y
CONFIG_BCMA_DRIVER_MIPS=y
CONFIG_BCMA_PFLASH=y
CONFIG_BCMA_SFLASH=y
CONFIG_BCMA_NFLASH=y
CONFIG_BCMA_DRIVER_GMAC_CMN=y
CONFIG_BCMA_DRIVER_GPIO=y
CONFIG_BCMA_DEBUG=y

#
# Multifunction device drivers
#
CONFIG_MFD_CORE=y
CONFIG_MFD_ACT8945A=y
CONFIG_MFD_AS3711=y
CONFIG_MFD_AS3722=y
CONFIG_PMIC_ADP5520=y
CONFIG_MFD_AAT2870_CORE=y
CONFIG_MFD_AT91_USART=y
CONFIG_MFD_ATMEL_FLEXCOM=y
CONFIG_MFD_ATMEL_HLCDC=y
CONFIG_MFD_ATMEL_SMC=y
CONFIG_MFD_BCM590XX=y
CONFIG_MFD_BD9571MWV=y
CONFIG_MFD_AXP20X=y
CONFIG_MFD_AXP20X_I2C=y
CONFIG_MFD_CROS_EC_DEV=y
CONFIG_MFD_MADERA=y
CONFIG_MFD_MADERA_I2C=y
CONFIG_MFD_MADERA_SPI=y
CONFIG_MFD_CS47L15=y
CONFIG_MFD_CS47L35=y
CONFIG_MFD_CS47L85=y
CONFIG_MFD_CS47L90=y
CONFIG_MFD_CS47L92=y
CONFIG_MFD_ASIC3=y
CONFIG_PMIC_DA903X=y
CONFIG_PMIC_DA9052=y
CONFIG_MFD_DA9052_SPI=y
CONFIG_MFD_DA9052_I2C=y
CONFIG_MFD_DA9055=y
CONFIG_MFD_DA9062=y
CONFIG_MFD_DA9063=y
CONFIG_MFD_DA9150=y
CONFIG_MFD_DLN2=y
CONFIG_MFD_ENE_KB3930=y
CONFIG_MFD_EXYNOS_LPASS=y
CONFIG_MFD_GATEWORKS_GSC=y
CONFIG_MFD_MC13XXX=y
CONFIG_MFD_MC13XXX_SPI=y
CONFIG_MFD_MC13XXX_I2C=y
CONFIG_MFD_MP2629=y
CONFIG_MFD_MXS_LRADC=y
CONFIG_MFD_MX25_TSADC=y
CONFIG_MFD_HI6421_PMIC=y
CONFIG_MFD_HI6421_SPMI=y
CONFIG_MFD_HI655X_PMIC=y
CONFIG_HTC_PASIC3=y
CONFIG_HTC_I2CPLD=y
CONFIG_LPC_ICH=y
CONFIG_LPC_SCH=y
CONFIG_INTEL_SOC_PMIC=y
CONFIG_MFD_IQS62X=y
CONFIG_MFD_JANZ_CMODIO=y
CONFIG_MFD_KEMPLD=y
CONFIG_MFD_88PM800=y
CONFIG_MFD_88PM805=y
CONFIG_MFD_88PM860X=y
CONFIG_MFD_MAX14577=y
CONFIG_MFD_MAX77620=y
CONFIG_MFD_MAX77650=y
CONFIG_MFD_MAX77686=y
CONFIG_MFD_MAX77693=y
CONFIG_MFD_MAX77714=y
CONFIG_MFD_MAX77843=y
CONFIG_MFD_MAX8907=y
CONFIG_MFD_MAX8925=y
CONFIG_MFD_MAX8997=y
CONFIG_MFD_MAX8998=y
CONFIG_MFD_MT6360=y
CONFIG_MFD_MT6370=y
CONFIG_MFD_MT6397=y
CONFIG_MFD_MENF21BMC=y
CONFIG_MFD_OCELOT=y
CONFIG_EZX_PCAP=y
CONFIG_MFD_CPCAP=y
CONFIG_MFD_VIPERBOARD=y
CONFIG_MFD_NTXEC=y
CONFIG_MFD_RETU=y
CONFIG_MFD_PCF50633=y
CONFIG_PCF50633_ADC=y
CONFIG_PCF50633_GPIO=y
CONFIG_UCB1400_CORE=y
CONFIG_MFD_PM8XXX=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_MFD_SY7636A=y
CONFIG_MFD_RDC321X=y
CONFIG_MFD_RT4831=y
CONFIG_MFD_RT5033=y
CONFIG_MFD_RT5120=y
CONFIG_MFD_RC5T583=y
CONFIG_MFD_RK808=y
CONFIG_MFD_RN5T618=y
CONFIG_MFD_SEC_CORE=y
CONFIG_MFD_SI476X_CORE=y
CONFIG_MFD_SIMPLE_MFD_I2C=y
CONFIG_MFD_SL28CPLD=y
CONFIG_MFD_SM501=y
CONFIG_MFD_SM501_GPIO=y
CONFIG_MFD_SKY81452=y
CONFIG_MFD_SC27XX_PMIC=y
CONFIG_ABX500_CORE=y
CONFIG_MFD_STMPE=y

#
# STMicroelectronics STMPE Interface Drivers
#
CONFIG_STMPE_I2C=y
CONFIG_STMPE_SPI=y
# end of STMicroelectronics STMPE Interface Drivers

CONFIG_MFD_SUN6I_PRCM=y
CONFIG_MFD_SYSCON=y
CONFIG_MFD_TI_AM335X_TSCADC=y
CONFIG_MFD_LP3943=y
CONFIG_MFD_LP8788=y
CONFIG_MFD_TI_LMU=y
CONFIG_MFD_OMAP_USB_HOST=y
CONFIG_MFD_PALMAS=y
CONFIG_TPS6105X=y
CONFIG_TPS65010=y
CONFIG_TPS6507X=y
CONFIG_MFD_TPS65086=y
CONFIG_MFD_TPS65090=y
CONFIG_MFD_TPS65217=y
CONFIG_MFD_TI_LP873X=y
CONFIG_MFD_TI_LP87565=y
CONFIG_MFD_TPS65218=y
CONFIG_MFD_TPS6586X=y
CONFIG_MFD_TPS65910=y
CONFIG_MFD_TPS65912=y
CONFIG_MFD_TPS65912_I2C=y
CONFIG_MFD_TPS65912_SPI=y
CONFIG_TWL4030_CORE=y
CONFIG_MFD_TWL4030_AUDIO=y
CONFIG_TWL6040_CORE=y
CONFIG_MFD_WL1273_CORE=y
CONFIG_MFD_LM3533=y
CONFIG_MFD_TIMBERDALE=y
CONFIG_MFD_TC3589X=y
CONFIG_MFD_TQMX86=y
CONFIG_MFD_VX855=y
CONFIG_MFD_LOCHNAGAR=y
CONFIG_MFD_ARIZONA=y
CONFIG_MFD_ARIZONA_I2C=y
CONFIG_MFD_ARIZONA_SPI=y
CONFIG_MFD_CS47L24=y
CONFIG_MFD_WM5102=y
CONFIG_MFD_WM5110=y
CONFIG_MFD_WM8997=y
CONFIG_MFD_WM8998=y
CONFIG_MFD_WM8400=y
CONFIG_MFD_WM831X=y
CONFIG_MFD_WM831X_I2C=y
CONFIG_MFD_WM831X_SPI=y
CONFIG_MFD_WM8350=y
CONFIG_MFD_WM8350_I2C=y
CONFIG_MFD_WM8994=y
CONFIG_MFD_STW481X=y
CONFIG_MFD_ROHM_BD718XX=y
CONFIG_MFD_ROHM_BD71828=y
CONFIG_MFD_ROHM_BD957XMUF=y
CONFIG_MFD_STM32_LPTIMER=y
CONFIG_MFD_STM32_TIMERS=y
CONFIG_MFD_STPMIC1=y
CONFIG_MFD_STMFX=y
CONFIG_MFD_WCD934X=y
CONFIG_MFD_ATC260X=y
CONFIG_MFD_ATC260X_I2C=y
CONFIG_MFD_KHADAS_MCU=y
CONFIG_MFD_ACER_A500_EC=y
CONFIG_MFD_QCOM_PM8008=y
CONFIG_RAVE_SP_CORE=y
CONFIG_MFD_INTEL_M10_BMC=y
CONFIG_MFD_RSMU_I2C=y
CONFIG_MFD_RSMU_SPI=y
# end of Multifunction device drivers

CONFIG_REGULATOR=y
CONFIG_REGULATOR_DEBUG=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
CONFIG_REGULATOR_USERSPACE_CONSUMER=y
CONFIG_REGULATOR_88PG86X=y
CONFIG_REGULATOR_88PM800=y
CONFIG_REGULATOR_88PM8607=y
CONFIG_REGULATOR_ACT8865=y
CONFIG_REGULATOR_ACT8945A=y
CONFIG_REGULATOR_AD5398=y
CONFIG_REGULATOR_ANATOP=y
CONFIG_REGULATOR_AAT2870=y
CONFIG_REGULATOR_ARIZONA_LDO1=y
CONFIG_REGULATOR_ARIZONA_MICSUPP=y
CONFIG_REGULATOR_ARM_SCMI=y
CONFIG_REGULATOR_AS3711=y
CONFIG_REGULATOR_AS3722=y
CONFIG_REGULATOR_ATC260X=y
CONFIG_REGULATOR_AXP20X=y
CONFIG_REGULATOR_BCM590XX=y
CONFIG_REGULATOR_BD71815=y
CONFIG_REGULATOR_BD71828=y
CONFIG_REGULATOR_BD718XX=y
CONFIG_REGULATOR_BD9571MWV=y
CONFIG_REGULATOR_BD957XMUF=y
CONFIG_REGULATOR_CPCAP=y
CONFIG_REGULATOR_CROS_EC=y
CONFIG_REGULATOR_DA903X=y
CONFIG_REGULATOR_DA9052=y
CONFIG_REGULATOR_DA9055=y
CONFIG_REGULATOR_DA9062=y
CONFIG_REGULATOR_DA9063=y
CONFIG_REGULATOR_DA9121=y
CONFIG_REGULATOR_DA9210=y
CONFIG_REGULATOR_DA9211=y
CONFIG_REGULATOR_FAN53555=y
CONFIG_REGULATOR_FAN53880=y
CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR_HI6421=y
CONFIG_REGULATOR_HI6421V530=y
CONFIG_REGULATOR_HI655X=y
CONFIG_REGULATOR_HI6421V600=y
CONFIG_REGULATOR_ISL9305=y
CONFIG_REGULATOR_ISL6271A=y
CONFIG_REGULATOR_LM363X=y
CONFIG_REGULATOR_LOCHNAGAR=y
CONFIG_REGULATOR_LP3971=y
CONFIG_REGULATOR_LP3972=y
CONFIG_REGULATOR_LP872X=y
CONFIG_REGULATOR_LP873X=y
CONFIG_REGULATOR_LP8755=y
CONFIG_REGULATOR_LP87565=y
CONFIG_REGULATOR_LP8788=y
CONFIG_REGULATOR_LTC3589=y
CONFIG_REGULATOR_LTC3676=y
CONFIG_REGULATOR_MAX14577=y
CONFIG_REGULATOR_MAX1586=y
CONFIG_REGULATOR_MAX77620=y
CONFIG_REGULATOR_MAX77650=y
CONFIG_REGULATOR_MAX8649=y
CONFIG_REGULATOR_MAX8660=y
CONFIG_REGULATOR_MAX8893=y
CONFIG_REGULATOR_MAX8907=y
CONFIG_REGULATOR_MAX8925=y
CONFIG_REGULATOR_MAX8952=y
CONFIG_REGULATOR_MAX8973=y
CONFIG_REGULATOR_MAX8997=y
CONFIG_REGULATOR_MAX8998=y
CONFIG_REGULATOR_MAX20086=y
CONFIG_REGULATOR_MAX77686=y
CONFIG_REGULATOR_MAX77693=y
CONFIG_REGULATOR_MAX77802=y
CONFIG_REGULATOR_MAX77826=y
CONFIG_REGULATOR_MC13XXX_CORE=y
CONFIG_REGULATOR_MC13783=y
CONFIG_REGULATOR_MC13892=y
CONFIG_REGULATOR_MCP16502=y
CONFIG_REGULATOR_MP5416=y
CONFIG_REGULATOR_MP8859=y
CONFIG_REGULATOR_MP886X=y
CONFIG_REGULATOR_MPQ7920=y
CONFIG_REGULATOR_MT6311=y
CONFIG_REGULATOR_MT6315=y
CONFIG_REGULATOR_MT6323=y
CONFIG_REGULATOR_MT6331=y
CONFIG_REGULATOR_MT6332=y
CONFIG_REGULATOR_MT6358=y
CONFIG_REGULATOR_MT6359=y
CONFIG_REGULATOR_MT6360=y
CONFIG_REGULATOR_MT6370=y
CONFIG_REGULATOR_MT6380=y
CONFIG_REGULATOR_MT6397=y
CONFIG_REGULATOR_PALMAS=y
CONFIG_REGULATOR_PBIAS=y
CONFIG_REGULATOR_PCA9450=y
CONFIG_REGULATOR_PCAP=y
CONFIG_REGULATOR_PCF50633=y
CONFIG_REGULATOR_PF8X00=y
CONFIG_REGULATOR_PFUZE100=y
CONFIG_REGULATOR_PV88060=y
CONFIG_REGULATOR_PV88080=y
CONFIG_REGULATOR_PV88090=y
CONFIG_REGULATOR_PWM=y
CONFIG_REGULATOR_QCOM_RPMH=y
CONFIG_REGULATOR_QCOM_SMD_RPM=y
CONFIG_REGULATOR_QCOM_SPMI=y
CONFIG_REGULATOR_QCOM_USB_VBUS=y
CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY=y
CONFIG_REGULATOR_RC5T583=y
CONFIG_REGULATOR_RK808=y
CONFIG_REGULATOR_RN5T618=y
CONFIG_REGULATOR_ROHM=y
CONFIG_REGULATOR_RT4801=y
CONFIG_REGULATOR_RT4831=y
CONFIG_REGULATOR_RT5033=y
CONFIG_REGULATOR_RT5120=y
CONFIG_REGULATOR_RT5190A=y
CONFIG_REGULATOR_RT5759=y
CONFIG_REGULATOR_RT6160=y
CONFIG_REGULATOR_RT6245=y
CONFIG_REGULATOR_RTQ2134=y
CONFIG_REGULATOR_RTMV20=y
CONFIG_REGULATOR_RTQ6752=y
CONFIG_REGULATOR_S2MPA01=y
CONFIG_REGULATOR_S2MPS11=y
CONFIG_REGULATOR_S5M8767=y
CONFIG_REGULATOR_SC2731=y
CONFIG_REGULATOR_SKY81452=y
CONFIG_REGULATOR_SLG51000=y
CONFIG_REGULATOR_STM32_BOOSTER=y
CONFIG_REGULATOR_STM32_VREFBUF=y
CONFIG_REGULATOR_STM32_PWR=y
CONFIG_REGULATOR_STPMIC1=y
CONFIG_REGULATOR_TI_ABB=y
CONFIG_REGULATOR_STW481X_VMMC=y
CONFIG_REGULATOR_SY7636A=y
CONFIG_REGULATOR_SY8106A=y
CONFIG_REGULATOR_SY8824X=y
CONFIG_REGULATOR_SY8827N=y
CONFIG_REGULATOR_TPS51632=y
CONFIG_REGULATOR_TPS6105X=y
CONFIG_REGULATOR_TPS62360=y
CONFIG_REGULATOR_TPS6286X=y
CONFIG_REGULATOR_TPS65023=y
CONFIG_REGULATOR_TPS6507X=y
CONFIG_REGULATOR_TPS65086=y
CONFIG_REGULATOR_TPS65090=y
CONFIG_REGULATOR_TPS65132=y
CONFIG_REGULATOR_TPS65217=y
CONFIG_REGULATOR_TPS65218=y
CONFIG_REGULATOR_TPS6524X=y
CONFIG_REGULATOR_TPS6586X=y
CONFIG_REGULATOR_TPS65910=y
CONFIG_REGULATOR_TPS65912=y
CONFIG_REGULATOR_TPS68470=y
CONFIG_REGULATOR_TWL4030=y
CONFIG_REGULATOR_UNIPHIER=y
CONFIG_REGULATOR_VCTRL=y
CONFIG_REGULATOR_WM831X=y
CONFIG_REGULATOR_WM8350=y
CONFIG_REGULATOR_WM8400=y
CONFIG_REGULATOR_WM8994=y
CONFIG_REGULATOR_QCOM_LABIBB=y
CONFIG_RC_CORE=y
CONFIG_BPF_LIRC_MODE2=y
CONFIG_LIRC=y
CONFIG_RC_MAP=y
CONFIG_RC_DECODERS=y
CONFIG_IR_IMON_DECODER=y
CONFIG_IR_JVC_DECODER=y
CONFIG_IR_MCE_KBD_DECODER=y
CONFIG_IR_NEC_DECODER=y
CONFIG_IR_RC5_DECODER=y
CONFIG_IR_RC6_DECODER=y
CONFIG_IR_RCMM_DECODER=y
CONFIG_IR_SANYO_DECODER=y
CONFIG_IR_SHARP_DECODER=y
CONFIG_IR_SONY_DECODER=y
CONFIG_IR_XMP_DECODER=y
CONFIG_RC_DEVICES=y
CONFIG_IR_ENE=y
CONFIG_IR_FINTEK=y
CONFIG_IR_GPIO_CIR=y
CONFIG_IR_GPIO_TX=y
CONFIG_IR_HIX5HD2=y
CONFIG_IR_IGORPLUGUSB=y
CONFIG_IR_IGUANA=y
CONFIG_IR_IMON=y
CONFIG_IR_IMON_RAW=y
CONFIG_IR_ITE_CIR=y
CONFIG_IR_MCEUSB=y
CONFIG_IR_MESON=y
CONFIG_IR_MESON_TX=y
CONFIG_IR_MTK=y
CONFIG_IR_NUVOTON=y
CONFIG_IR_PWM_TX=y
CONFIG_IR_REDRAT3=y
CONFIG_IR_RX51=y
CONFIG_IR_SERIAL=y
CONFIG_IR_SERIAL_TRANSMITTER=y
CONFIG_IR_SPI=y
CONFIG_IR_STREAMZAP=y
CONFIG_IR_SUNXI=y
CONFIG_IR_TOY=y
CONFIG_IR_TTUSBIR=y
CONFIG_IR_WINBOND_CIR=y
CONFIG_RC_ATI_REMOTE=y
CONFIG_RC_LOOPBACK=y
CONFIG_RC_ST=y
CONFIG_RC_XBOX_DVD=y
CONFIG_IR_IMG=y
CONFIG_IR_IMG_RAW=y
CONFIG_IR_IMG_HW=y
CONFIG_IR_IMG_NEC=y
CONFIG_IR_IMG_JVC=y
CONFIG_IR_IMG_SONY=y
CONFIG_IR_IMG_SHARP=y
CONFIG_IR_IMG_SANYO=y
CONFIG_IR_IMG_RC5=y
CONFIG_IR_IMG_RC6=y
CONFIG_CEC_CORE=y
CONFIG_CEC_NOTIFIER=y
CONFIG_CEC_PIN=y

#
# CEC support
#
CONFIG_MEDIA_CEC_RC=y
CONFIG_CEC_PIN_ERROR_INJ=y
CONFIG_MEDIA_CEC_SUPPORT=y
CONFIG_CEC_CH7322=y
CONFIG_CEC_CROS_EC=y
CONFIG_CEC_MESON_AO=y
CONFIG_CEC_MESON_G12A_AO=y
CONFIG_CEC_GPIO=y
CONFIG_CEC_SAMSUNG_S5P=y
CONFIG_CEC_STI=y
CONFIG_CEC_STM32=y
CONFIG_CEC_TEGRA=y
CONFIG_USB_PULSE8_CEC=y
CONFIG_USB_RAINSHADOW_CEC=y
# end of CEC support

CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_SUPPORT_FILTER=y
CONFIG_MEDIA_SUBDRV_AUTOSELECT=y

#
# Media device types
#
CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
CONFIG_MEDIA_RADIO_SUPPORT=y
CONFIG_MEDIA_SDR_SUPPORT=y
CONFIG_MEDIA_PLATFORM_SUPPORT=y
CONFIG_MEDIA_TEST_SUPPORT=y
# end of Media device types

CONFIG_VIDEO_DEV=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_DVB_CORE=y

#
# Video4Linux options
#
CONFIG_VIDEO_V4L2_I2C=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_VIDEO_ADV_DEBUG=y
CONFIG_VIDEO_FIXED_MINOR_RANGES=y
CONFIG_VIDEO_TUNER=y
CONFIG_V4L2_JPEG_HELPER=y
CONFIG_V4L2_H264=y
CONFIG_V4L2_VP9=y
CONFIG_V4L2_MEM2MEM_DEV=y
CONFIG_V4L2_FLASH_LED_CLASS=y
CONFIG_V4L2_FWNODE=y
CONFIG_V4L2_ASYNC=y
CONFIG_VIDEOBUF_GEN=y
CONFIG_VIDEOBUF_DMA_SG=y
CONFIG_VIDEOBUF_VMALLOC=y
CONFIG_VIDEOBUF_DMA_CONTIG=y
# end of Video4Linux options

#
# Media controller options
#
CONFIG_MEDIA_CONTROLLER_DVB=y
CONFIG_MEDIA_CONTROLLER_REQUEST_API=y
# end of Media controller options

#
# Digital TV options
#
CONFIG_DVB_MMAP=y
CONFIG_DVB_NET=y
CONFIG_DVB_MAX_ADAPTERS=16
CONFIG_DVB_DYNAMIC_MINORS=y
CONFIG_DVB_DEMUX_SECTION_LOSS_LOG=y
CONFIG_DVB_ULE_DEBUG=y
# end of Digital TV options

#
# Media drivers
#

#
# Drivers filtered as selected at 'Filter media drivers'
#

#
# Media drivers
#
CONFIG_MEDIA_USB_SUPPORT=y

#
# Webcam devices
#
CONFIG_USB_GSPCA=y
CONFIG_USB_GSPCA_BENQ=y
CONFIG_USB_GSPCA_CONEX=y
CONFIG_USB_GSPCA_CPIA1=y
CONFIG_USB_GSPCA_DTCS033=y
CONFIG_USB_GSPCA_ETOMS=y
CONFIG_USB_GSPCA_FINEPIX=y
CONFIG_USB_GSPCA_JEILINJ=y
CONFIG_USB_GSPCA_JL2005BCD=y
CONFIG_USB_GSPCA_KINECT=y
CONFIG_USB_GSPCA_KONICA=y
CONFIG_USB_GSPCA_MARS=y
CONFIG_USB_GSPCA_MR97310A=y
CONFIG_USB_GSPCA_NW80X=y
CONFIG_USB_GSPCA_OV519=y
CONFIG_USB_GSPCA_OV534=y
CONFIG_USB_GSPCA_OV534_9=y
CONFIG_USB_GSPCA_PAC207=y
CONFIG_USB_GSPCA_PAC7302=y
CONFIG_USB_GSPCA_PAC7311=y
CONFIG_USB_GSPCA_SE401=y
CONFIG_USB_GSPCA_SN9C2028=y
CONFIG_USB_GSPCA_SN9C20X=y
CONFIG_USB_GSPCA_SONIXB=y
CONFIG_USB_GSPCA_SONIXJ=y
CONFIG_USB_GSPCA_SPCA1528=y
CONFIG_USB_GSPCA_SPCA500=y
CONFIG_USB_GSPCA_SPCA501=y
CONFIG_USB_GSPCA_SPCA505=y
CONFIG_USB_GSPCA_SPCA506=y
CONFIG_USB_GSPCA_SPCA508=y
CONFIG_USB_GSPCA_SPCA561=y
CONFIG_USB_GSPCA_SQ905=y
CONFIG_USB_GSPCA_SQ905C=y
CONFIG_USB_GSPCA_SQ930X=y
CONFIG_USB_GSPCA_STK014=y
CONFIG_USB_GSPCA_STK1135=y
CONFIG_USB_GSPCA_STV0680=y
CONFIG_USB_GSPCA_SUNPLUS=y
CONFIG_USB_GSPCA_T613=y
CONFIG_USB_GSPCA_TOPRO=y
CONFIG_USB_GSPCA_TOUPTEK=y
CONFIG_USB_GSPCA_TV8532=y
CONFIG_USB_GSPCA_VC032X=y
CONFIG_USB_GSPCA_VICAM=y
CONFIG_USB_GSPCA_XIRLINK_CIT=y
CONFIG_USB_GSPCA_ZC3XX=y
CONFIG_USB_GL860=y
CONFIG_USB_M5602=y
CONFIG_USB_STV06XX=y
CONFIG_USB_PWC=y
CONFIG_USB_PWC_DEBUG=y
CONFIG_USB_PWC_INPUT_EVDEV=y
CONFIG_USB_S2255=y
CONFIG_VIDEO_USBTV=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y

#
# Analog TV USB devices
#
CONFIG_VIDEO_GO7007=y
CONFIG_VIDEO_GO7007_USB=y
CONFIG_VIDEO_GO7007_LOADER=y
CONFIG_VIDEO_GO7007_USB_S2250_BOARD=y
CONFIG_VIDEO_HDPVR=y
CONFIG_VIDEO_PVRUSB2=y
CONFIG_VIDEO_PVRUSB2_SYSFS=y
CONFIG_VIDEO_PVRUSB2_DVB=y
CONFIG_VIDEO_PVRUSB2_DEBUGIFC=y
CONFIG_VIDEO_STK1160_COMMON=y
CONFIG_VIDEO_STK1160=y

#
# Analog/digital TV USB devices
#
CONFIG_VIDEO_AU0828=y
CONFIG_VIDEO_AU0828_V4L2=y
CONFIG_VIDEO_AU0828_RC=y
CONFIG_VIDEO_CX231XX=y
CONFIG_VIDEO_CX231XX_RC=y
CONFIG_VIDEO_CX231XX_ALSA=y
CONFIG_VIDEO_CX231XX_DVB=y

#
# Digital TV USB devices
#
CONFIG_DVB_AS102=y
CONFIG_DVB_B2C2_FLEXCOP_USB=y
CONFIG_DVB_B2C2_FLEXCOP_USB_DEBUG=y
CONFIG_DVB_USB_V2=y
CONFIG_DVB_USB_AF9015=y
CONFIG_DVB_USB_AF9035=y
CONFIG_DVB_USB_ANYSEE=y
CONFIG_DVB_USB_AU6610=y
CONFIG_DVB_USB_AZ6007=y
CONFIG_DVB_USB_CE6230=y
CONFIG_DVB_USB_DVBSKY=y
CONFIG_DVB_USB_EC168=y
CONFIG_DVB_USB_GL861=y
CONFIG_DVB_USB_LME2510=y
CONFIG_DVB_USB_MXL111SF=y
CONFIG_DVB_USB_RTL28XXU=y
CONFIG_DVB_USB_ZD1301=y
CONFIG_DVB_USB=y
CONFIG_DVB_USB_DEBUG=y
CONFIG_DVB_USB_A800=y
CONFIG_DVB_USB_AF9005=y
CONFIG_DVB_USB_AF9005_REMOTE=y
CONFIG_DVB_USB_AZ6027=y
CONFIG_DVB_USB_CINERGY_T2=y
CONFIG_DVB_USB_CXUSB=y
CONFIG_DVB_USB_CXUSB_ANALOG=y
CONFIG_DVB_USB_DIB0700=y
CONFIG_DVB_USB_DIB3000MC=y
CONFIG_DVB_USB_DIBUSB_MB=y
CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y
CONFIG_DVB_USB_DIBUSB_MC=y
CONFIG_DVB_USB_DIGITV=y
CONFIG_DVB_USB_DTT200U=y
CONFIG_DVB_USB_DTV5100=y
CONFIG_DVB_USB_DW2102=y
CONFIG_DVB_USB_GP8PSK=y
CONFIG_DVB_USB_M920X=y
CONFIG_DVB_USB_NOVA_T_USB2=y
CONFIG_DVB_USB_OPERA1=y
CONFIG_DVB_USB_PCTV452E=y
CONFIG_DVB_USB_TECHNISAT_USB2=y
CONFIG_DVB_USB_TTUSB2=y
CONFIG_DVB_USB_UMT_010=y
CONFIG_DVB_USB_VP702X=y
CONFIG_DVB_USB_VP7045=y
CONFIG_SMS_USB_DRV=y
CONFIG_DVB_TTUSB_BUDGET=y
CONFIG_DVB_TTUSB_DEC=y

#
# Webcam, TV (analog/digital) USB devices
#
CONFIG_VIDEO_EM28XX=y
CONFIG_VIDEO_EM28XX_V4L2=y
CONFIG_VIDEO_EM28XX_ALSA=y
CONFIG_VIDEO_EM28XX_DVB=y
CONFIG_VIDEO_EM28XX_RC=y

#
# Software defined radio USB devices
#
CONFIG_USB_AIRSPY=y
CONFIG_USB_HACKRF=y
CONFIG_USB_MSI2500=y
CONFIG_MEDIA_PCI_SUPPORT=y

#
# Media capture support
#
CONFIG_VIDEO_SOLO6X10=y
CONFIG_STA2X11_VIP=y
CONFIG_VIDEO_TW5864=y
CONFIG_VIDEO_TW68=y
CONFIG_VIDEO_TW686X=y
CONFIG_VIDEO_ZORAN=y
CONFIG_VIDEO_ZORAN_DC30=y
CONFIG_VIDEO_ZORAN_ZR36060=y
CONFIG_VIDEO_ZORAN_BUZ=y
CONFIG_VIDEO_ZORAN_DC10=y
CONFIG_VIDEO_ZORAN_LML33=y
CONFIG_VIDEO_ZORAN_LML33R10=y
CONFIG_VIDEO_ZORAN_AVS6EYES=y

#
# Media capture/analog TV support
#
CONFIG_VIDEO_DT3155=y
CONFIG_VIDEO_IVTV=y
CONFIG_VIDEO_IVTV_ALSA=y
CONFIG_VIDEO_FB_IVTV=y

#
# Media capture/analog/hybrid TV support
#
CONFIG_VIDEO_BT848=y
CONFIG_DVB_BT8XX=y
CONFIG_VIDEO_COBALT=y
CONFIG_VIDEO_CX18=y
CONFIG_VIDEO_CX18_ALSA=y
CONFIG_VIDEO_CX23885=y
CONFIG_MEDIA_ALTERA_CI=y
CONFIG_VIDEO_CX25821=y
CONFIG_VIDEO_CX25821_ALSA=y
CONFIG_VIDEO_CX88=y
CONFIG_VIDEO_CX88_ALSA=y
CONFIG_VIDEO_CX88_BLACKBIRD=y
CONFIG_VIDEO_CX88_DVB=y
CONFIG_VIDEO_CX88_ENABLE_VP3054=y
CONFIG_VIDEO_CX88_VP3054=y
CONFIG_VIDEO_CX88_MPEG=y
CONFIG_VIDEO_SAA7134=y
CONFIG_VIDEO_SAA7134_ALSA=y
CONFIG_VIDEO_SAA7134_RC=y
CONFIG_VIDEO_SAA7134_DVB=y
CONFIG_VIDEO_SAA7134_GO7007=y
CONFIG_VIDEO_SAA7164=y

#
# Media digital TV PCI Adapters
#
CONFIG_DVB_B2C2_FLEXCOP_PCI=y
CONFIG_DVB_B2C2_FLEXCOP_PCI_DEBUG=y
CONFIG_DVB_DDBRIDGE=y
CONFIG_DVB_DDBRIDGE_MSIENABLE=y
CONFIG_DVB_DM1105=y
CONFIG_MANTIS_CORE=y
CONFIG_DVB_MANTIS=y
CONFIG_DVB_HOPPER=y
CONFIG_DVB_NETUP_UNIDVB=y
CONFIG_DVB_NGENE=y
CONFIG_DVB_PLUTO2=y
CONFIG_DVB_PT1=y
CONFIG_DVB_PT3=y
CONFIG_DVB_SMIPCIE=y
CONFIG_RADIO_ADAPTERS=y
CONFIG_RADIO_MAXIRADIO=y
CONFIG_RADIO_SAA7706H=y
CONFIG_RADIO_SHARK=y
CONFIG_RADIO_SHARK2=y
CONFIG_RADIO_SI4713=y
CONFIG_RADIO_SI476X=y
CONFIG_RADIO_TEA575X=y
CONFIG_RADIO_TEA5764=y
CONFIG_RADIO_TEA5764_XTAL=y
CONFIG_RADIO_TEF6862=y
CONFIG_RADIO_TIMBERDALE=y
CONFIG_RADIO_WL1273=y
CONFIG_USB_DSBR=y
CONFIG_USB_KEENE=y
CONFIG_USB_MA901=y
CONFIG_USB_MR800=y
CONFIG_USB_RAREMONO=y
CONFIG_RADIO_SI470X=y
CONFIG_USB_SI470X=y
CONFIG_I2C_SI470X=y
CONFIG_USB_SI4713=y
CONFIG_PLATFORM_SI4713=y
CONFIG_I2C_SI4713=y
CONFIG_RADIO_WL128X=y
CONFIG_V4L_RADIO_ISA_DRIVERS=y
CONFIG_RADIO_AZTECH=y
CONFIG_RADIO_AZTECH_PORT=350
CONFIG_RADIO_CADET=y
CONFIG_RADIO_GEMTEK=y
CONFIG_RADIO_GEMTEK_PORT=34c
CONFIG_RADIO_GEMTEK_PROBE=y
CONFIG_RADIO_ISA=y
CONFIG_RADIO_RTRACK=y
CONFIG_RADIO_RTRACK2=y
CONFIG_RADIO_RTRACK2_PORT=30c
CONFIG_RADIO_RTRACK_PORT=30f
CONFIG_RADIO_SF16FMI=y
CONFIG_RADIO_SF16FMR2=y
CONFIG_RADIO_TERRATEC=y
CONFIG_RADIO_TRUST=y
CONFIG_RADIO_TRUST_PORT=350
CONFIG_RADIO_TYPHOON=y
CONFIG_RADIO_TYPHOON_MUTEFREQ=87500
CONFIG_RADIO_TYPHOON_PORT=316
CONFIG_RADIO_ZOLTRIX=y
CONFIG_RADIO_ZOLTRIX_PORT=20c
CONFIG_MEDIA_PLATFORM_DRIVERS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_SDR_PLATFORM_DRIVERS=y
CONFIG_DVB_PLATFORM_DRIVERS=y
CONFIG_V4L_MEM2MEM_DRIVERS=y
CONFIG_VIDEO_MEM2MEM_DEINTERLACE=y
CONFIG_VIDEO_MUX=y

#
# Allegro DVT media platform drivers
#
CONFIG_VIDEO_ALLEGRO_DVT=y

#
# Amlogic media platform drivers
#
CONFIG_VIDEO_MESON_GE2D=y

#
# Amphion drivers
#
CONFIG_VIDEO_AMPHION_VPU=y

#
# Aspeed media platform drivers
#
CONFIG_VIDEO_ASPEED=y

#
# Atmel media platform drivers
#
CONFIG_VIDEO_ATMEL_ISC=y
CONFIG_VIDEO_ATMEL_XISC=y
CONFIG_VIDEO_ATMEL_ISC_BASE=y
CONFIG_VIDEO_ATMEL_ISI=y
CONFIG_VIDEO_MICROCHIP_CSI2DC=y

#
# Cadence media platform drivers
#
CONFIG_VIDEO_CADENCE_CSI2RX=y
CONFIG_VIDEO_CADENCE_CSI2TX=y

#
# Chips&Media media platform drivers
#
CONFIG_VIDEO_CODA=y
CONFIG_VIDEO_IMX_VDOA=y

#
# Intel media platform drivers
#
CONFIG_VIDEO_PXA27x=y

#
# Marvell media platform drivers
#
CONFIG_VIDEO_CAFE_CCIC=y
CONFIG_VIDEO_MMP_CAMERA=y

#
# Mediatek media platform drivers
#
CONFIG_VIDEO_MEDIATEK_JPEG=y
CONFIG_VIDEO_MEDIATEK_MDP=y
CONFIG_VIDEO_MEDIATEK_VCODEC_SCP=y
CONFIG_VIDEO_MEDIATEK_VCODEC_VPU=y
CONFIG_VIDEO_MEDIATEK_VCODEC=y
CONFIG_VIDEO_MEDIATEK_VPU=y
CONFIG_VIDEO_MEDIATEK_MDP3=y

#
# NVidia media platform drivers
#
CONFIG_VIDEO_TEGRA_VDE=y

#
# NXP media platform drivers
#
CONFIG_VIDEO_IMX_MIPI_CSIS=y
CONFIG_VIDEO_IMX_PXP=y
CONFIG_VIDEO_MX2_EMMAPRP=y
CONFIG_VIDEO_DW100=y
CONFIG_VIDEO_IMX8_JPEG=y

#
# Qualcomm media platform drivers
#
CONFIG_VIDEO_QCOM_CAMSS=y
CONFIG_VIDEO_QCOM_VENUS=y

#
# Renesas media platform drivers
#
CONFIG_VIDEO_RENESAS_CEU=y
CONFIG_VIDEO_RCAR_ISP=y
CONFIG_VIDEO_SH_VOU=y
CONFIG_VIDEO_RCAR_CSI2=y
CONFIG_VIDEO_RCAR_VIN=y
CONFIG_VIDEO_RENESAS_FCP=y
CONFIG_VIDEO_RENESAS_FDP1=y
CONFIG_VIDEO_RENESAS_JPU=y
CONFIG_VIDEO_RENESAS_VSP1=y
CONFIG_VIDEO_RCAR_DRIF=y

#
# Rockchip media platform drivers
#
CONFIG_VIDEO_ROCKCHIP_RGA=y
CONFIG_VIDEO_ROCKCHIP_ISP1=y

#
# Samsung media platform drivers
#
CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=y
CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS=y
CONFIG_VIDEO_EXYNOS4_IS_COMMON=y
CONFIG_VIDEO_S5P_FIMC=y
CONFIG_VIDEO_S5P_MIPI_CSIS=y
CONFIG_VIDEO_EXYNOS_FIMC_LITE=y
CONFIG_VIDEO_EXYNOS4_FIMC_IS=y
CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE=y
CONFIG_VIDEO_S3C_CAMIF=y
CONFIG_VIDEO_SAMSUNG_S5P_G2D=y
CONFIG_VIDEO_SAMSUNG_S5P_JPEG=y
CONFIG_VIDEO_SAMSUNG_S5P_MFC=y

#
# STMicroelectronics media platform drivers
#
CONFIG_VIDEO_STI_BDISP=y
CONFIG_DVB_C8SECTPFE=y
CONFIG_VIDEO_STI_DELTA=y
CONFIG_VIDEO_STI_DELTA_MJPEG=y
CONFIG_VIDEO_STI_DELTA_DRIVER=y
CONFIG_VIDEO_STI_HVA=y
CONFIG_VIDEO_STI_HVA_DEBUGFS=y
CONFIG_VIDEO_STM32_DCMI=y
CONFIG_VIDEO_STM32_DMA2D=y

#
# Sunxi media platform drivers
#
CONFIG_VIDEO_SUN4I_CSI=y
CONFIG_VIDEO_SUN6I_CSI=y
CONFIG_VIDEO_SUN6I_MIPI_CSI2=y
CONFIG_VIDEO_SUN8I_A83T_MIPI_CSI2=y
CONFIG_VIDEO_SUN8I_DEINTERLACE=y
CONFIG_VIDEO_SUN8I_ROTATE=y

#
# Texas Instruments drivers
#
CONFIG_VIDEO_TI_VPDMA=y
CONFIG_VIDEO_TI_SC=y
CONFIG_VIDEO_TI_CSC=y
CONFIG_VIDEO_TI_CAL=y
CONFIG_VIDEO_TI_CAL_MC=y
CONFIG_VIDEO_TI_VPE=y
CONFIG_VIDEO_TI_VPE_DEBUG=y
CONFIG_VIDEO_AM437X_VPFE=y
CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY=y
CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE=y
CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY=y
CONFIG_VIDEO_OMAP2_VOUT_VRFB=y
CONFIG_VIDEO_OMAP2_VOUT=y
CONFIG_VIDEO_OMAP3=y
CONFIG_VIDEO_OMAP3_DEBUG=y

#
# Verisilicon media platform drivers
#
CONFIG_VIDEO_HANTRO=y
CONFIG_VIDEO_HANTRO_IMX8M=y
CONFIG_VIDEO_HANTRO_SAMA5D4=y
CONFIG_VIDEO_HANTRO_ROCKCHIP=y
CONFIG_VIDEO_HANTRO_SUNXI=y

#
# VIA media platform drivers
#
CONFIG_VIDEO_VIA_CAMERA=y

#
# Xilinx media platform drivers
#
CONFIG_VIDEO_XILINX=y
CONFIG_VIDEO_XILINX_CSI2RXSS=y
CONFIG_VIDEO_XILINX_TPG=y
CONFIG_VIDEO_XILINX_VTC=y

#
# MMC/SDIO DVB adapters
#
CONFIG_SMS_SDIO_DRV=y
CONFIG_V4L_TEST_DRIVERS=y
CONFIG_VIDEO_VIM2M=y
CONFIG_VIDEO_VICODEC=y
CONFIG_VIDEO_VIMC=y
CONFIG_DVB_TEST_DRIVERS=y
CONFIG_DVB_VIDTV=y

#
# FireWire (IEEE 1394) Adapters
#
CONFIG_DVB_FIREDTV=y
CONFIG_DVB_FIREDTV_INPUT=y
CONFIG_MEDIA_COMMON_OPTIONS=y

#
# common driver options
#
CONFIG_CYPRESS_FIRMWARE=y
CONFIG_TTPCI_EEPROM=y
CONFIG_VIDEO_CX2341X=y
CONFIG_VIDEO_TVEEPROM=y
CONFIG_DVB_B2C2_FLEXCOP=y
CONFIG_DVB_B2C2_FLEXCOP_DEBUG=y
CONFIG_SMS_SIANO_MDTV=y
CONFIG_SMS_SIANO_RC=y
CONFIG_SMS_SIANO_DEBUGFS=y
CONFIG_VIDEO_V4L2_TPG=y
CONFIG_VIDEOBUF2_CORE=y
CONFIG_VIDEOBUF2_V4L2=y
CONFIG_VIDEOBUF2_MEMOPS=y
CONFIG_VIDEOBUF2_DMA_CONTIG=y
CONFIG_VIDEOBUF2_VMALLOC=y
CONFIG_VIDEOBUF2_DMA_SG=y
CONFIG_VIDEOBUF2_DVB=y
# end of Media drivers

#
# Media ancillary drivers
#
CONFIG_MEDIA_ATTACH=y

#
# IR I2C driver auto-selected by 'Autoselect ancillary drivers'
#
CONFIG_VIDEO_IR_I2C=y

#
# Camera sensor devices
#
CONFIG_VIDEO_APTINA_PLL=y
CONFIG_VIDEO_CCS_PLL=y
CONFIG_VIDEO_AR0521=y
CONFIG_VIDEO_HI556=y
CONFIG_VIDEO_HI846=y
CONFIG_VIDEO_HI847=y
CONFIG_VIDEO_IMX208=y
CONFIG_VIDEO_IMX214=y
CONFIG_VIDEO_IMX219=y
CONFIG_VIDEO_IMX258=y
CONFIG_VIDEO_IMX274=y
CONFIG_VIDEO_IMX290=y
CONFIG_VIDEO_IMX319=y
CONFIG_VIDEO_IMX334=y
CONFIG_VIDEO_IMX335=y
CONFIG_VIDEO_IMX355=y
CONFIG_VIDEO_IMX412=y
CONFIG_VIDEO_MAX9271_LIB=y
CONFIG_VIDEO_MT9M001=y
CONFIG_VIDEO_MT9M032=y
CONFIG_VIDEO_MT9M111=y
CONFIG_VIDEO_MT9P031=y
CONFIG_VIDEO_MT9T001=y
CONFIG_VIDEO_MT9T112=y
CONFIG_VIDEO_MT9V011=y
CONFIG_VIDEO_MT9V032=y
CONFIG_VIDEO_MT9V111=y
CONFIG_VIDEO_NOON010PC30=y
CONFIG_VIDEO_OG01A1B=y
CONFIG_VIDEO_OV02A10=y
CONFIG_VIDEO_OV08D10=y
CONFIG_VIDEO_OV13858=y
CONFIG_VIDEO_OV13B10=y
CONFIG_VIDEO_OV2640=y
CONFIG_VIDEO_OV2659=y
CONFIG_VIDEO_OV2680=y
CONFIG_VIDEO_OV2685=y
CONFIG_VIDEO_OV2740=y
CONFIG_VIDEO_OV5640=y
CONFIG_VIDEO_OV5645=y
CONFIG_VIDEO_OV5647=y
CONFIG_VIDEO_OV5648=y
CONFIG_VIDEO_OV5670=y
CONFIG_VIDEO_OV5675=y
CONFIG_VIDEO_OV5693=y
CONFIG_VIDEO_OV5695=y
CONFIG_VIDEO_OV6650=y
CONFIG_VIDEO_OV7251=y
CONFIG_VIDEO_OV7640=y
CONFIG_VIDEO_OV7670=y
CONFIG_VIDEO_OV772X=y
CONFIG_VIDEO_OV7740=y
CONFIG_VIDEO_OV8856=y
CONFIG_VIDEO_OV8865=y
CONFIG_VIDEO_OV9282=y
CONFIG_VIDEO_OV9640=y
CONFIG_VIDEO_OV9650=y
CONFIG_VIDEO_OV9734=y
CONFIG_VIDEO_RDACM20=y
CONFIG_VIDEO_RDACM21=y
CONFIG_VIDEO_RJ54N1=y
CONFIG_VIDEO_S5C73M3=y
CONFIG_VIDEO_S5K4ECGX=y
CONFIG_VIDEO_S5K5BAF=y
CONFIG_VIDEO_S5K6A3=y
CONFIG_VIDEO_S5K6AA=y
CONFIG_VIDEO_SR030PC30=y
CONFIG_VIDEO_VS6624=y
CONFIG_VIDEO_CCS=y
CONFIG_VIDEO_ET8EK8=y
CONFIG_VIDEO_M5MOLS=y
# end of Camera sensor devices

#
# Lens drivers
#
CONFIG_VIDEO_AD5820=y
CONFIG_VIDEO_AK7375=y
CONFIG_VIDEO_DW9714=y
CONFIG_VIDEO_DW9768=y
CONFIG_VIDEO_DW9807_VCM=y
# end of Lens drivers

#
# Flash devices
#
CONFIG_VIDEO_ADP1653=y
CONFIG_VIDEO_LM3560=y
CONFIG_VIDEO_LM3646=y
# end of Flash devices

#
# Audio decoders, processors and mixers
#
CONFIG_VIDEO_CS3308=y
CONFIG_VIDEO_CS5345=y
CONFIG_VIDEO_CS53L32A=y
CONFIG_VIDEO_MSP3400=y
CONFIG_VIDEO_SONY_BTF_MPX=y
CONFIG_VIDEO_TDA1997X=y
CONFIG_VIDEO_TDA7432=y
CONFIG_VIDEO_TDA9840=y
CONFIG_VIDEO_TEA6415C=y
CONFIG_VIDEO_TEA6420=y
CONFIG_VIDEO_TLV320AIC23B=y
CONFIG_VIDEO_TVAUDIO=y
CONFIG_VIDEO_UDA1342=y
CONFIG_VIDEO_VP27SMPX=y
CONFIG_VIDEO_WM8739=y
CONFIG_VIDEO_WM8775=y
# end of Audio decoders, processors and mixers

#
# RDS decoders
#
CONFIG_VIDEO_SAA6588=y
# end of RDS decoders

#
# Video decoders
#
CONFIG_VIDEO_ADV7180=y
CONFIG_VIDEO_ADV7183=y
CONFIG_VIDEO_ADV748X=y
CONFIG_VIDEO_ADV7604=y
CONFIG_VIDEO_ADV7604_CEC=y
CONFIG_VIDEO_ADV7842=y
CONFIG_VIDEO_ADV7842_CEC=y
CONFIG_VIDEO_BT819=y
CONFIG_VIDEO_BT856=y
CONFIG_VIDEO_BT866=y
CONFIG_VIDEO_ISL7998X=y
CONFIG_VIDEO_KS0127=y
CONFIG_VIDEO_MAX9286=y
CONFIG_VIDEO_ML86V7667=y
CONFIG_VIDEO_SAA7110=y
CONFIG_VIDEO_SAA711X=y
CONFIG_VIDEO_TC358743=y
CONFIG_VIDEO_TC358743_CEC=y
CONFIG_VIDEO_TVP514X=y
CONFIG_VIDEO_TVP5150=y
CONFIG_VIDEO_TVP7002=y
CONFIG_VIDEO_TW2804=y
CONFIG_VIDEO_TW9903=y
CONFIG_VIDEO_TW9906=y
CONFIG_VIDEO_TW9910=y
CONFIG_VIDEO_VPX3220=y

#
# Video and audio decoders
#
CONFIG_VIDEO_SAA717X=y
CONFIG_VIDEO_CX25840=y
# end of Video decoders

#
# Video encoders
#
CONFIG_VIDEO_AD9389B=y
CONFIG_VIDEO_ADV7170=y
CONFIG_VIDEO_ADV7175=y
CONFIG_VIDEO_ADV7343=y
CONFIG_VIDEO_ADV7393=y
CONFIG_VIDEO_ADV7511=y
CONFIG_VIDEO_ADV7511_CEC=y
CONFIG_VIDEO_AK881X=y
CONFIG_VIDEO_SAA7127=y
CONFIG_VIDEO_SAA7185=y
CONFIG_VIDEO_THS8200=y
# end of Video encoders

#
# Video improvement chips
#
CONFIG_VIDEO_UPD64031A=y
CONFIG_VIDEO_UPD64083=y
# end of Video improvement chips

#
# Audio/Video compression chips
#
CONFIG_VIDEO_SAA6752HS=y
# end of Audio/Video compression chips

#
# SDR tuner chips
#
CONFIG_SDR_MAX2175=y
# end of SDR tuner chips

#
# Miscellaneous helper chips
#
CONFIG_VIDEO_I2C=y
CONFIG_VIDEO_M52790=y
CONFIG_VIDEO_ST_MIPID02=y
CONFIG_VIDEO_THS7303=y
# end of Miscellaneous helper chips

#
# Media SPI Adapters
#
CONFIG_CXD2880_SPI_DRV=y
CONFIG_VIDEO_GS1662=y
# end of Media SPI Adapters

CONFIG_MEDIA_TUNER=y

#
# Customize TV tuners
#
CONFIG_MEDIA_TUNER_E4000=y
CONFIG_MEDIA_TUNER_FC0011=y
CONFIG_MEDIA_TUNER_FC0012=y
CONFIG_MEDIA_TUNER_FC0013=y
CONFIG_MEDIA_TUNER_FC2580=y
CONFIG_MEDIA_TUNER_IT913X=y
CONFIG_MEDIA_TUNER_M88RS6000T=y
CONFIG_MEDIA_TUNER_MAX2165=y
CONFIG_MEDIA_TUNER_MC44S803=y
CONFIG_MEDIA_TUNER_MSI001=y
CONFIG_MEDIA_TUNER_MT2060=y
CONFIG_MEDIA_TUNER_MT2063=y
CONFIG_MEDIA_TUNER_MT20XX=y
CONFIG_MEDIA_TUNER_MT2131=y
CONFIG_MEDIA_TUNER_MT2266=y
CONFIG_MEDIA_TUNER_MXL301RF=y
CONFIG_MEDIA_TUNER_MXL5005S=y
CONFIG_MEDIA_TUNER_MXL5007T=y
CONFIG_MEDIA_TUNER_QM1D1B0004=y
CONFIG_MEDIA_TUNER_QM1D1C0042=y
CONFIG_MEDIA_TUNER_QT1010=y
CONFIG_MEDIA_TUNER_R820T=y
CONFIG_MEDIA_TUNER_SI2157=y
CONFIG_MEDIA_TUNER_SIMPLE=y
CONFIG_MEDIA_TUNER_TDA18212=y
CONFIG_MEDIA_TUNER_TDA18218=y
CONFIG_MEDIA_TUNER_TDA18250=y
CONFIG_MEDIA_TUNER_TDA18271=y
CONFIG_MEDIA_TUNER_TDA827X=y
CONFIG_MEDIA_TUNER_TDA8290=y
CONFIG_MEDIA_TUNER_TDA9887=y
CONFIG_MEDIA_TUNER_TEA5761=y
CONFIG_MEDIA_TUNER_TEA5767=y
CONFIG_MEDIA_TUNER_TUA9001=y
CONFIG_MEDIA_TUNER_XC2028=y
CONFIG_MEDIA_TUNER_XC4000=y
CONFIG_MEDIA_TUNER_XC5000=y
# end of Customize TV tuners

#
# Customise DVB Frontends
#

#
# Multistandard (satellite) frontends
#
CONFIG_DVB_M88DS3103=y
CONFIG_DVB_MXL5XX=y
CONFIG_DVB_STB0899=y
CONFIG_DVB_STB6100=y
CONFIG_DVB_STV090x=y
CONFIG_DVB_STV0910=y
CONFIG_DVB_STV6110x=y
CONFIG_DVB_STV6111=y

#
# Multistandard (cable + terrestrial) frontends
#
CONFIG_DVB_DRXK=y
CONFIG_DVB_MN88472=y
CONFIG_DVB_MN88473=y
CONFIG_DVB_SI2165=y
CONFIG_DVB_TDA18271C2DD=y

#
# DVB-S (satellite) frontends
#
CONFIG_DVB_CX24110=y
CONFIG_DVB_CX24116=y
CONFIG_DVB_CX24117=y
CONFIG_DVB_CX24120=y
CONFIG_DVB_CX24123=y
CONFIG_DVB_DS3000=y
CONFIG_DVB_MB86A16=y
CONFIG_DVB_MT312=y
CONFIG_DVB_S5H1420=y
CONFIG_DVB_SI21XX=y
CONFIG_DVB_STB6000=y
CONFIG_DVB_STV0288=y
CONFIG_DVB_STV0299=y
CONFIG_DVB_STV0900=y
CONFIG_DVB_STV6110=y
CONFIG_DVB_TDA10071=y
CONFIG_DVB_TDA10086=y
CONFIG_DVB_TDA8083=y
CONFIG_DVB_TDA8261=y
CONFIG_DVB_TDA826X=y
CONFIG_DVB_TS2020=y
CONFIG_DVB_TUA6100=y
CONFIG_DVB_TUNER_CX24113=y
CONFIG_DVB_TUNER_ITD1000=y
CONFIG_DVB_VES1X93=y
CONFIG_DVB_ZL10036=y
CONFIG_DVB_ZL10039=y

#
# DVB-T (terrestrial) frontends
#
CONFIG_DVB_AF9013=y
CONFIG_DVB_AS102_FE=y
CONFIG_DVB_CX22700=y
CONFIG_DVB_CX22702=y
CONFIG_DVB_CXD2820R=y
CONFIG_DVB_CXD2841ER=y
CONFIG_DVB_DIB3000MB=y
CONFIG_DVB_DIB3000MC=y
CONFIG_DVB_DIB7000M=y
CONFIG_DVB_DIB7000P=y
CONFIG_DVB_DIB9000=y
CONFIG_DVB_DRXD=y
CONFIG_DVB_EC100=y
CONFIG_DVB_GP8PSK_FE=y
CONFIG_DVB_L64781=y
CONFIG_DVB_MT352=y
CONFIG_DVB_NXT6000=y
CONFIG_DVB_RTL2830=y
CONFIG_DVB_RTL2832=y
CONFIG_DVB_RTL2832_SDR=y
CONFIG_DVB_S5H1432=y
CONFIG_DVB_SI2168=y
CONFIG_DVB_SP887X=y
CONFIG_DVB_STV0367=y
CONFIG_DVB_TDA10048=y
CONFIG_DVB_TDA1004X=y
CONFIG_DVB_ZD1301_DEMOD=y
CONFIG_DVB_ZL10353=y
CONFIG_DVB_CXD2880=y

#
# DVB-C (cable) frontends
#
CONFIG_DVB_STV0297=y
CONFIG_DVB_TDA10021=y
CONFIG_DVB_TDA10023=y
CONFIG_DVB_VES1820=y

#
# ATSC (North American/Korean Terrestrial/Cable DTV) frontends
#
CONFIG_DVB_AU8522=y
CONFIG_DVB_AU8522_DTV=y
CONFIG_DVB_AU8522_V4L=y
CONFIG_DVB_BCM3510=y
CONFIG_DVB_LG2160=y
CONFIG_DVB_LGDT3305=y
CONFIG_DVB_LGDT3306A=y
CONFIG_DVB_LGDT330X=y
CONFIG_DVB_MXL692=y
CONFIG_DVB_NXT200X=y
CONFIG_DVB_OR51132=y
CONFIG_DVB_OR51211=y
CONFIG_DVB_S5H1409=y
CONFIG_DVB_S5H1411=y

#
# ISDB-T (terrestrial) frontends
#
CONFIG_DVB_DIB8000=y
CONFIG_DVB_MB86A20S=y
CONFIG_DVB_S921=y

#
# ISDB-S (satellite) & ISDB-T (terrestrial) frontends
#
CONFIG_DVB_MN88443X=y
CONFIG_DVB_TC90522=y

#
# Digital terrestrial only tuners/PLL
#
CONFIG_DVB_PLL=y
CONFIG_DVB_TUNER_DIB0070=y
CONFIG_DVB_TUNER_DIB0090=y

#
# SEC control devices for DVB-S
#
CONFIG_DVB_A8293=y
CONFIG_DVB_AF9033=y
CONFIG_DVB_ASCOT2E=y
CONFIG_DVB_ATBM8830=y
CONFIG_DVB_HELENE=y
CONFIG_DVB_HORUS3A=y
CONFIG_DVB_ISL6405=y
CONFIG_DVB_ISL6421=y
CONFIG_DVB_ISL6423=y
CONFIG_DVB_IX2505V=y
CONFIG_DVB_LGS8GL5=y
CONFIG_DVB_LGS8GXX=y
CONFIG_DVB_LNBH25=y
CONFIG_DVB_LNBH29=y
CONFIG_DVB_LNBP21=y
CONFIG_DVB_LNBP22=y
CONFIG_DVB_M88RS2000=y
CONFIG_DVB_TDA665x=y
CONFIG_DVB_DRX39XYJ=y

#
# Common Interface (EN50221) controller drivers
#
CONFIG_DVB_CXD2099=y
CONFIG_DVB_SP2=y
# end of Customise DVB Frontends

#
# Tools to develop new frontends
#
CONFIG_DVB_DUMMY_FE=y
# end of Media ancillary drivers

#
# Graphics support
#
CONFIG_APERTURE_HELPERS=y
CONFIG_IMX_IPUV3_CORE=y
CONFIG_DRM=y
CONFIG_DRM_MIPI_DBI=y
CONFIG_DRM_MIPI_DSI=y
CONFIG_DRM_DEBUG_MM=y
CONFIG_DRM_USE_DYNAMIC_DEBUG=y
CONFIG_DRM_KUNIT_TEST=y
CONFIG_DRM_KMS_HELPER=y
CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS=y
CONFIG_DRM_DEBUG_MODESET_LOCK=y
CONFIG_DRM_FBDEV_EMULATION=y
CONFIG_DRM_FBDEV_OVERALLOC=100
CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM=y
CONFIG_DRM_LOAD_EDID_FIRMWARE=y
CONFIG_DRM_DP_AUX_BUS=y
CONFIG_DRM_DISPLAY_HELPER=y
CONFIG_DRM_DISPLAY_DP_HELPER=y
CONFIG_DRM_DISPLAY_HDCP_HELPER=y
CONFIG_DRM_DISPLAY_HDMI_HELPER=y
CONFIG_DRM_DP_AUX_CHARDEV=y
CONFIG_DRM_DP_CEC=y
CONFIG_DRM_TTM=y
CONFIG_DRM_BUDDY=y
CONFIG_DRM_VRAM_HELPER=y
CONFIG_DRM_TTM_HELPER=y
CONFIG_DRM_GEM_DMA_HELPER=y
CONFIG_DRM_GEM_SHMEM_HELPER=y
CONFIG_DRM_SCHED=y

#
# I2C encoder or helper chips
#
CONFIG_DRM_I2C_CH7006=y
CONFIG_DRM_I2C_SIL164=y
CONFIG_DRM_I2C_NXP_TDA998X=y
CONFIG_DRM_I2C_NXP_TDA9950=y
# end of I2C encoder or helper chips

#
# ARM devices
#
CONFIG_DRM_HDLCD=y
CONFIG_DRM_HDLCD_SHOW_UNDERRUN=y
CONFIG_DRM_MALI_DISPLAY=y
CONFIG_DRM_KOMEDA=y
# end of ARM devices

CONFIG_DRM_RADEON=y
CONFIG_DRM_RADEON_USERPTR=y
CONFIG_DRM_AMDGPU=y
CONFIG_DRM_AMDGPU_SI=y
CONFIG_DRM_AMDGPU_CIK=y
CONFIG_DRM_AMDGPU_USERPTR=y

#
# ACP (Audio CoProcessor) Configuration
#
CONFIG_DRM_AMD_ACP=y
# end of ACP (Audio CoProcessor) Configuration

#
# Display Engine Configuration
#
CONFIG_DRM_AMD_DC=y
CONFIG_DRM_AMD_DC_HDCP=y
CONFIG_DRM_AMD_DC_SI=y
CONFIG_DEBUG_KERNEL_DC=y
# end of Display Engine Configuration

CONFIG_DRM_NOUVEAU=y
CONFIG_NOUVEAU_LEGACY_CTX_SUPPORT=y
CONFIG_NOUVEAU_DEBUG=5
CONFIG_NOUVEAU_DEBUG_DEFAULT=3
CONFIG_NOUVEAU_DEBUG_MMU=y
CONFIG_NOUVEAU_DEBUG_PUSH=y
CONFIG_DRM_NOUVEAU_BACKLIGHT=y
CONFIG_DRM_KMB_DISPLAY=y
CONFIG_DRM_VGEM=y
CONFIG_DRM_VKMS=y
CONFIG_DRM_EXYNOS=y

#
# CRTCs
#
CONFIG_DRM_EXYNOS5433_DECON=y
CONFIG_DRM_EXYNOS_MIXER=y
CONFIG_DRM_EXYNOS_VIDI=y

#
# Encoders and Bridges
#
CONFIG_DRM_EXYNOS_DSI=y
CONFIG_DRM_EXYNOS_HDMI=y
CONFIG_DRM_EXYNOS_MIC=y

#
# Sub-drivers
#
CONFIG_DRM_EXYNOS_G2D=y
CONFIG_DRM_EXYNOS_IPP=y
CONFIG_DRM_EXYNOS_FIMC=y
CONFIG_DRM_EXYNOS_ROTATOR=y
CONFIG_DRM_EXYNOS_SCALER=y
CONFIG_DRM_EXYNOS_GSC=y
CONFIG_DRM_ROCKCHIP=y
CONFIG_ROCKCHIP_VOP=y
CONFIG_ROCKCHIP_VOP2=y
CONFIG_ROCKCHIP_ANALOGIX_DP=y
CONFIG_ROCKCHIP_CDN_DP=y
CONFIG_ROCKCHIP_DW_HDMI=y
CONFIG_ROCKCHIP_DW_MIPI_DSI=y
CONFIG_ROCKCHIP_INNO_HDMI=y
CONFIG_ROCKCHIP_LVDS=y
CONFIG_ROCKCHIP_RGB=y
CONFIG_ROCKCHIP_RK3066_HDMI=y
CONFIG_DRM_UDL=y
CONFIG_DRM_AST=y
CONFIG_DRM_MGAG200=y
CONFIG_DRM_RCAR_DW_HDMI=y
CONFIG_DRM_RCAR_USE_LVDS=y
CONFIG_DRM_RCAR_USE_MIPI_DSI=y
CONFIG_DRM_SUN4I=y
CONFIG_DRM_SUN4I_HDMI=y
CONFIG_DRM_SUN4I_HDMI_CEC=y
CONFIG_DRM_SUN4I_BACKEND=y
CONFIG_DRM_SUN6I_DSI=y
CONFIG_DRM_SUN8I_DW_HDMI=y
CONFIG_DRM_SUN8I_MIXER=y
CONFIG_DRM_SUN8I_TCON_TOP=y
CONFIG_DRM_QXL=y
CONFIG_DRM_VIRTIO_GPU=y
CONFIG_DRM_MSM=y
CONFIG_DRM_MSM_GPU_STATE=y
CONFIG_DRM_MSM_GPU_SUDO=y
CONFIG_DRM_MSM_MDSS=y
CONFIG_DRM_MSM_MDP4=y
CONFIG_DRM_MSM_MDP5=y
CONFIG_DRM_MSM_DPU=y
CONFIG_DRM_MSM_DP=y
CONFIG_DRM_MSM_DSI=y
CONFIG_DRM_MSM_DSI_28NM_PHY=y
CONFIG_DRM_MSM_DSI_20NM_PHY=y
CONFIG_DRM_MSM_DSI_28NM_8960_PHY=y
CONFIG_DRM_MSM_DSI_14NM_PHY=y
CONFIG_DRM_MSM_DSI_10NM_PHY=y
CONFIG_DRM_MSM_DSI_7NM_PHY=y
CONFIG_DRM_MSM_HDMI=y
CONFIG_DRM_MSM_HDMI_HDCP=y
CONFIG_DRM_PANEL=y

#
# Display Panels
#
CONFIG_DRM_PANEL_ABT_Y030XX067A=y
CONFIG_DRM_PANEL_ARM_VERSATILE=y
CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596=y
CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0=y
CONFIG_DRM_PANEL_BOE_HIMAX8279D=y
CONFIG_DRM_PANEL_BOE_TV101WUM_NL6=y
CONFIG_DRM_PANEL_DSI_CM=y
CONFIG_DRM_PANEL_LVDS=y
CONFIG_DRM_PANEL_SIMPLE=y
CONFIG_DRM_PANEL_EDP=y
CONFIG_DRM_PANEL_EBBG_FT8719=y
CONFIG_DRM_PANEL_ELIDA_KD35T133=y
CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02=y
CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D=y
CONFIG_DRM_PANEL_ILITEK_IL9322=y
CONFIG_DRM_PANEL_ILITEK_ILI9341=y
CONFIG_DRM_PANEL_ILITEK_ILI9881C=y
CONFIG_DRM_PANEL_INNOLUX_EJ030NA=y
CONFIG_DRM_PANEL_INNOLUX_P079ZCA=y
CONFIG_DRM_PANEL_JDI_LT070ME05000=y
CONFIG_DRM_PANEL_JDI_R63452=y
CONFIG_DRM_PANEL_KHADAS_TS050=y
CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04=y
CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W=y
CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829=y
CONFIG_DRM_PANEL_SAMSUNG_LD9040=y
CONFIG_DRM_PANEL_LG_LB035Q02=y
CONFIG_DRM_PANEL_LG_LG4573=y
CONFIG_DRM_PANEL_NEC_NL8048HL11=y
CONFIG_DRM_PANEL_NEWVISION_NV3052C=y
CONFIG_DRM_PANEL_NOVATEK_NT35510=y
CONFIG_DRM_PANEL_NOVATEK_NT35560=y
CONFIG_DRM_PANEL_NOVATEK_NT35950=y
CONFIG_DRM_PANEL_NOVATEK_NT36672A=y
CONFIG_DRM_PANEL_NOVATEK_NT39016=y
CONFIG_DRM_PANEL_MANTIX_MLAF057WE51=y
CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO=y
CONFIG_DRM_PANEL_ORISETECH_OTM8009A=y
CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS=y
CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00=y
CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=y
CONFIG_DRM_PANEL_RAYDIUM_RM67191=y
CONFIG_DRM_PANEL_RAYDIUM_RM68200=y
CONFIG_DRM_PANEL_RONBO_RB070D30=y
CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20=y
CONFIG_DRM_PANEL_SAMSUNG_DB7430=y
CONFIG_DRM_PANEL_SAMSUNG_S6D16D0=y
CONFIG_DRM_PANEL_SAMSUNG_S6D27A1=y
CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2=y
CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03=y
CONFIG_DRM_PANEL_SAMSUNG_S6E63M0=y
CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_SPI=y
CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI=y
CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01=y
CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=y
CONFIG_DRM_PANEL_SAMSUNG_SOFEF00=y
CONFIG_DRM_PANEL_SEIKO_43WVF1G=y
CONFIG_DRM_PANEL_SHARP_LQ101R1SX01=y
CONFIG_DRM_PANEL_SHARP_LS037V7DW01=y
CONFIG_DRM_PANEL_SHARP_LS043T1LE01=y
CONFIG_DRM_PANEL_SHARP_LS060T1SX01=y
CONFIG_DRM_PANEL_SITRONIX_ST7701=y
CONFIG_DRM_PANEL_SITRONIX_ST7703=y
CONFIG_DRM_PANEL_SITRONIX_ST7789V=y
CONFIG_DRM_PANEL_SONY_ACX565AKM=y
CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521=y
CONFIG_DRM_PANEL_TDO_TL070WSH30=y
CONFIG_DRM_PANEL_TPO_TD028TTEC1=y
CONFIG_DRM_PANEL_TPO_TD043MTEA1=y
CONFIG_DRM_PANEL_TPO_TPG110=y
CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA=y
CONFIG_DRM_PANEL_VISIONOX_RM69299=y
CONFIG_DRM_PANEL_WIDECHIPS_WS2401=y
CONFIG_DRM_PANEL_XINPENG_XPP055C272=y
# end of Display Panels

CONFIG_DRM_BRIDGE=y
CONFIG_DRM_PANEL_BRIDGE=y

#
# Display Interface Bridges
#
CONFIG_DRM_CDNS_DSI=y
CONFIG_DRM_CHIPONE_ICN6211=y
CONFIG_DRM_CHRONTEL_CH7033=y
CONFIG_DRM_CROS_EC_ANX7688=y
CONFIG_DRM_DISPLAY_CONNECTOR=y
CONFIG_DRM_FSL_LDB=y
CONFIG_DRM_ITE_IT6505=y
CONFIG_DRM_LONTIUM_LT8912B=y
CONFIG_DRM_LONTIUM_LT9211=y
CONFIG_DRM_LONTIUM_LT9611=y
CONFIG_DRM_LONTIUM_LT9611UXC=y
CONFIG_DRM_ITE_IT66121=y
CONFIG_DRM_LVDS_CODEC=y
CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW=y
CONFIG_DRM_NWL_MIPI_DSI=y
CONFIG_DRM_NXP_PTN3460=y
CONFIG_DRM_PARADE_PS8622=y
CONFIG_DRM_PARADE_PS8640=y
CONFIG_DRM_SIL_SII8620=y
CONFIG_DRM_SII902X=y
CONFIG_DRM_SII9234=y
CONFIG_DRM_SIMPLE_BRIDGE=y
CONFIG_DRM_THINE_THC63LVD1024=y
CONFIG_DRM_TOSHIBA_TC358762=y
CONFIG_DRM_TOSHIBA_TC358764=y
CONFIG_DRM_TOSHIBA_TC358767=y
CONFIG_DRM_TOSHIBA_TC358768=y
CONFIG_DRM_TOSHIBA_TC358775=y
CONFIG_DRM_TI_DLPC3433=y
CONFIG_DRM_TI_TFP410=y
CONFIG_DRM_TI_SN65DSI83=y
CONFIG_DRM_TI_SN65DSI86=y
CONFIG_DRM_TI_TPD12S015=y
CONFIG_DRM_ANALOGIX_ANX6345=y
CONFIG_DRM_ANALOGIX_ANX78XX=y
CONFIG_DRM_ANALOGIX_DP=y
CONFIG_DRM_ANALOGIX_ANX7625=y
CONFIG_DRM_I2C_ADV7511=y
CONFIG_DRM_I2C_ADV7511_AUDIO=y
CONFIG_DRM_I2C_ADV7511_CEC=y
CONFIG_DRM_CDNS_MHDP8546=y
CONFIG_DRM_CDNS_MHDP8546_J721E=y
CONFIG_DRM_IMX8QM_LDB=y
CONFIG_DRM_IMX8QXP_LDB=y
CONFIG_DRM_IMX8QXP_PIXEL_COMBINER=y
CONFIG_DRM_IMX8QXP_PIXEL_LINK=y
CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI=y
CONFIG_DRM_DW_HDMI=y
CONFIG_DRM_DW_HDMI_AHB_AUDIO=y
CONFIG_DRM_DW_HDMI_I2S_AUDIO=y
CONFIG_DRM_DW_HDMI_GP_AUDIO=y
CONFIG_DRM_DW_HDMI_CEC=y
CONFIG_DRM_DW_MIPI_DSI=y
# end of Display Interface Bridges

CONFIG_DRM_IMX=y
CONFIG_DRM_IMX_PARALLEL_DISPLAY=y
CONFIG_DRM_IMX_TVE=y
CONFIG_DRM_IMX_LDB=y
CONFIG_DRM_IMX_HDMI=y
CONFIG_DRM_INGENIC=y
CONFIG_DRM_INGENIC_IPU=y
CONFIG_DRM_V3D=y
CONFIG_DRM_VC4=y
CONFIG_DRM_VC4_HDMI_CEC=y
CONFIG_DRM_ETNAVIV=y
CONFIG_DRM_ETNAVIV_THERMAL=y
CONFIG_DRM_HISI_HIBMC=y
CONFIG_DRM_LOGICVC=y
CONFIG_DRM_MXS=y
CONFIG_DRM_MXSFB=y
CONFIG_DRM_IMX_LCDIF=y
CONFIG_DRM_ARCPGU=y
CONFIG_DRM_BOCHS=y
CONFIG_DRM_CIRRUS_QEMU=y
CONFIG_DRM_GM12U320=y
CONFIG_DRM_PANEL_MIPI_DBI=y
CONFIG_DRM_SIMPLEDRM=y
CONFIG_TINYDRM_HX8357D=y
CONFIG_TINYDRM_ILI9163=y
CONFIG_TINYDRM_ILI9225=y
CONFIG_TINYDRM_ILI9341=y
CONFIG_TINYDRM_ILI9486=y
CONFIG_TINYDRM_MI0283QT=y
CONFIG_TINYDRM_REPAPER=y
CONFIG_TINYDRM_ST7586=y
CONFIG_TINYDRM_ST7735R=y
CONFIG_DRM_PL111=y
CONFIG_DRM_TVE200=y
CONFIG_DRM_LIMA=y
CONFIG_DRM_PANFROST=y
CONFIG_DRM_ASPEED_GFX=y
CONFIG_DRM_MCDE=y
CONFIG_DRM_TIDSS=y
CONFIG_DRM_ZYNQMP_DPSUB=y
CONFIG_DRM_GUD=y
CONFIG_DRM_SSD130X=y
CONFIG_DRM_SSD130X_I2C=y
CONFIG_DRM_SSD130X_SPI=y
CONFIG_DRM_SPRD=y
CONFIG_DRM_LEGACY=y
CONFIG_DRM_TDFX=y
CONFIG_DRM_R128=y
CONFIG_DRM_MGA=y
CONFIG_DRM_VIA=y
CONFIG_DRM_SAVAGE=y
CONFIG_DRM_EXPORT_FOR_TESTS=y
CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y
CONFIG_DRM_NOMODESET=y
CONFIG_DRM_LIB_RANDOM=y

#
# Frame buffer Devices
#
CONFIG_FB_CMDLINE=y
CONFIG_FB_NOTIFY=y
CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_DDC=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
CONFIG_FB_CFB_REV_PIXELS_IN_BYTE=y
CONFIG_FB_SYS_FILLRECT=y
CONFIG_FB_SYS_COPYAREA=y
CONFIG_FB_SYS_IMAGEBLIT=y
CONFIG_FB_FOREIGN_ENDIAN=y
CONFIG_FB_BOTH_ENDIAN=y
# CONFIG_FB_BIG_ENDIAN is not set
# CONFIG_FB_LITTLE_ENDIAN is not set
CONFIG_FB_SYS_FOPS=y
CONFIG_FB_DEFERRED_IO=y
CONFIG_FB_SVGALIB=y
CONFIG_FB_MACMODES=y
CONFIG_FB_BACKLIGHT=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_FB_TILEBLITTING=y

#
# Frame buffer hardware drivers
#
CONFIG_FB_GRVGA=y
CONFIG_FB_CIRRUS=y
CONFIG_FB_PM2=y
CONFIG_FB_PM2_FIFO_DISCONNECT=y
CONFIG_FB_CLPS711X=y
CONFIG_FB_IMX=y
CONFIG_FB_ARC=y
CONFIG_FB_CONTROL=y
CONFIG_FB_ASILIANT=y
CONFIG_FB_IMSTT=y
CONFIG_FB_UVESA=y
CONFIG_FB_GBE=y
CONFIG_FB_GBE_MEM=4
CONFIG_FB_SBUS=y
CONFIG_FB_BW2=y
CONFIG_FB_CG3=y
CONFIG_FB_CG6=y
CONFIG_FB_FFB=y
CONFIG_FB_TCX=y
CONFIG_FB_CG14=y
CONFIG_FB_P9100=y
CONFIG_FB_LEO=y
CONFIG_FB_XVR500=y
CONFIG_FB_XVR2500=y
CONFIG_FB_XVR1000=y
CONFIG_FB_PVR2=y
CONFIG_FB_OPENCORES=y
CONFIG_FB_S1D13XXX=y
CONFIG_FB_ATMEL=y
CONFIG_FB_NVIDIA=y
CONFIG_FB_NVIDIA_I2C=y
CONFIG_FB_NVIDIA_DEBUG=y
CONFIG_FB_NVIDIA_BACKLIGHT=y
CONFIG_FB_RIVA=y
CONFIG_FB_RIVA_I2C=y
CONFIG_FB_RIVA_DEBUG=y
CONFIG_FB_RIVA_BACKLIGHT=y
CONFIG_FB_I740=y
CONFIG_FB_MATROX=y
CONFIG_FB_MATROX_MILLENIUM=y
CONFIG_FB_MATROX_MYSTIQUE=y
CONFIG_FB_MATROX_G=y
CONFIG_FB_MATROX_I2C=y
CONFIG_FB_MATROX_MAVEN=y
CONFIG_FB_RADEON=y
CONFIG_FB_RADEON_I2C=y
CONFIG_FB_RADEON_BACKLIGHT=y
CONFIG_FB_RADEON_DEBUG=y
CONFIG_FB_ATY128=y
CONFIG_FB_ATY128_BACKLIGHT=y
CONFIG_FB_ATY=y
CONFIG_FB_ATY_CT=y
CONFIG_FB_ATY_GENERIC_LCD=y
CONFIG_FB_ATY_GX=y
CONFIG_FB_ATY_BACKLIGHT=y
CONFIG_FB_S3=y
CONFIG_FB_S3_DDC=y
CONFIG_FB_SAVAGE=y
CONFIG_FB_SAVAGE_I2C=y
CONFIG_FB_SAVAGE_ACCEL=y
CONFIG_FB_SIS=y
CONFIG_FB_SIS_300=y
CONFIG_FB_SIS_315=y
CONFIG_FB_VIA=y
CONFIG_FB_VIA_DIRECT_PROCFS=y
CONFIG_FB_VIA_X_COMPATIBILITY=y
CONFIG_FB_NEOMAGIC=y
CONFIG_FB_KYRO=y
CONFIG_FB_3DFX=y
CONFIG_FB_3DFX_ACCEL=y
CONFIG_FB_3DFX_I2C=y
CONFIG_FB_VOODOO1=y
CONFIG_FB_VT8623=y
CONFIG_FB_TRIDENT=y
CONFIG_FB_ARK=y
CONFIG_FB_PM3=y
CONFIG_FB_CARMINE=y
CONFIG_FB_CARMINE_DRAM_EVAL=y
# CONFIG_CARMINE_DRAM_CUSTOM is not set
CONFIG_FB_WM8505=y
CONFIG_FB_WMT_GE_ROPS=y
CONFIG_FB_PXA168=y
CONFIG_FB_W100=y
CONFIG_FB_SH_MOBILE_LCDC=y
CONFIG_FB_TMIO=y
CONFIG_FB_TMIO_ACCELL=y
CONFIG_FB_S3C=y
CONFIG_FB_S3C_DEBUG_REGWRITE=y
CONFIG_FB_SM501=y
CONFIG_FB_SMSCUFX=y
CONFIG_FB_UDL=y
CONFIG_FB_IBM_GXT4500=y
CONFIG_FB_GOLDFISH=y
CONFIG_FB_DA8XX=y
CONFIG_FB_VIRTUAL=y
CONFIG_FB_METRONOME=y
CONFIG_FB_MB862XX=y
CONFIG_FB_MB862XX_PCI_GDC=y
CONFIG_FB_MB862XX_I2C=y
CONFIG_FB_BROADSHEET=y
CONFIG_FB_SSD1307=y
CONFIG_FB_SM712=y
CONFIG_FB_OMAP_LCD_H3=y
CONFIG_FB_OMAP2=y
CONFIG_FB_OMAP2_DEBUG_SUPPORT=y
CONFIG_FB_OMAP2_NUM_FBS=3
CONFIG_FB_OMAP2_DSS_INIT=y
CONFIG_FB_OMAP2_DSS=y
CONFIG_FB_OMAP2_DSS_DEBUG=y
CONFIG_FB_OMAP2_DSS_DEBUGFS=y
CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS=y
CONFIG_FB_OMAP2_DSS_DPI=y
CONFIG_FB_OMAP2_DSS_VENC=y
CONFIG_FB_OMAP2_DSS_HDMI_COMMON=y
CONFIG_FB_OMAP4_DSS_HDMI=y
CONFIG_FB_OMAP5_DSS_HDMI=y
CONFIG_FB_OMAP2_DSS_SDI=y
CONFIG_FB_OMAP2_DSS_DSI=y
CONFIG_FB_OMAP2_DSS_MIN_FCK_PER_PCK=0
CONFIG_FB_OMAP2_DSS_SLEEP_AFTER_VENC_RESET=y

#
# OMAPFB Panel and Encoder Drivers
#
CONFIG_FB_OMAP2_ENCODER_OPA362=y
CONFIG_FB_OMAP2_ENCODER_TFP410=y
CONFIG_FB_OMAP2_ENCODER_TPD12S015=y
CONFIG_FB_OMAP2_CONNECTOR_DVI=y
CONFIG_FB_OMAP2_CONNECTOR_HDMI=y
CONFIG_FB_OMAP2_CONNECTOR_ANALOG_TV=y
CONFIG_FB_OMAP2_PANEL_DPI=y
CONFIG_FB_OMAP2_PANEL_LGPHILIPS_LB035Q02=y
# end of OMAPFB Panel and Encoder Drivers

CONFIG_MMP_DISP=y
CONFIG_MMP_DISP_CONTROLLER=y
CONFIG_MMP_DISP_SPI=y
CONFIG_MMP_PANEL_TPOHVGA=y
CONFIG_MMP_FB=y
# end of Frame buffer Devices

#
# Backlight & LCD device support
#
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_L4F00242T03=y
CONFIG_LCD_LMS283GF05=y
CONFIG_LCD_LTV350QV=y
CONFIG_LCD_ILI922X=y
CONFIG_LCD_ILI9320=y
CONFIG_LCD_TDO24M=y
CONFIG_LCD_VGG2432A4=y
CONFIG_LCD_PLATFORM=y
CONFIG_LCD_AMS369FG06=y
CONFIG_LCD_LMS501KF03=y
CONFIG_LCD_HX8357=y
CONFIG_LCD_OTM3225A=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_ATMEL_LCDC=y
CONFIG_BACKLIGHT_KTD253=y
CONFIG_BACKLIGHT_LM3533=y
CONFIG_BACKLIGHT_OMAP1=y
CONFIG_BACKLIGHT_PWM=y
CONFIG_BACKLIGHT_DA903X=y
CONFIG_BACKLIGHT_DA9052=y
CONFIG_BACKLIGHT_MAX8925=y
CONFIG_BACKLIGHT_MT6370=y
CONFIG_BACKLIGHT_QCOM_WLED=y
CONFIG_BACKLIGHT_RT4831=y
CONFIG_BACKLIGHT_WM831X=y
CONFIG_BACKLIGHT_ADP5520=y
CONFIG_BACKLIGHT_ADP8860=y
CONFIG_BACKLIGHT_ADP8870=y
CONFIG_BACKLIGHT_88PM860X=y
CONFIG_BACKLIGHT_PCF50633=y
CONFIG_BACKLIGHT_AAT2870=y
CONFIG_BACKLIGHT_LM3630A=y
CONFIG_BACKLIGHT_LM3639=y
CONFIG_BACKLIGHT_LP855X=y
CONFIG_BACKLIGHT_LP8788=y
CONFIG_BACKLIGHT_PANDORA=y
CONFIG_BACKLIGHT_SKY81452=y
CONFIG_BACKLIGHT_TPS65217=y
CONFIG_BACKLIGHT_AS3711=y
CONFIG_BACKLIGHT_GPIO=y
CONFIG_BACKLIGHT_LV5207LP=y
CONFIG_BACKLIGHT_BD6107=y
CONFIG_BACKLIGHT_ARCXCNN=y
CONFIG_BACKLIGHT_RAVE_SP=y
CONFIG_BACKLIGHT_LED=y
# end of Backlight & LCD device support

CONFIG_VGASTATE=y
CONFIG_VIDEOMODE_HELPERS=y
CONFIG_HDMI=y

#
# Console display driver support
#
CONFIG_DUMMY_CONSOLE=y
CONFIG_DUMMY_CONSOLE_COLUMNS=80
CONFIG_DUMMY_CONSOLE_ROWS=25
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y
# end of Console display driver support

CONFIG_LOGO=y
CONFIG_LOGO_LINUX_MONO=y
CONFIG_LOGO_LINUX_VGA16=y
CONFIG_LOGO_LINUX_CLUT224=y
CONFIG_LOGO_SUN_CLUT224=y
# end of Graphics support

CONFIG_SOUND=y
CONFIG_SOUND_OSS_CORE=y
CONFIG_SOUND_OSS_CORE_PRECLAIM=y
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
CONFIG_SND_PCM_ELD=y
CONFIG_SND_PCM_IEC958=y
CONFIG_SND_DMAENGINE_PCM=y
CONFIG_SND_HWDEP=y
CONFIG_SND_SEQ_DEVICE=y
CONFIG_SND_RAWMIDI=y
CONFIG_SND_COMPRESS_OFFLOAD=y
CONFIG_SND_JACK=y
CONFIG_SND_JACK_INPUT_DEV=y
CONFIG_SND_OSSEMUL=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
CONFIG_SND_PCM_OSS_PLUGINS=y
CONFIG_SND_PCM_TIMER=y
CONFIG_SND_HRTIMER=y
CONFIG_SND_DYNAMIC_MINORS=y
CONFIG_SND_MAX_CARDS=32
CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_PROC_FS=y
CONFIG_SND_VERBOSE_PROCFS=y
CONFIG_SND_VERBOSE_PRINTK=y
CONFIG_SND_CTL_FAST_LOOKUP=y
CONFIG_SND_DEBUG=y
CONFIG_SND_DEBUG_VERBOSE=y
CONFIG_SND_PCM_XRUN_DEBUG=y
CONFIG_SND_CTL_INPUT_VALIDATION=y
CONFIG_SND_CTL_DEBUG=y
CONFIG_SND_JACK_INJECTION_DEBUG=y
CONFIG_SND_VMASTER=y
CONFIG_SND_CTL_LED=y
CONFIG_SND_SEQUENCER=y
CONFIG_SND_SEQ_DUMMY=y
CONFIG_SND_SEQUENCER_OSS=y
CONFIG_SND_SEQ_HRTIMER_DEFAULT=y
CONFIG_SND_SEQ_MIDI_EVENT=y
CONFIG_SND_SEQ_MIDI=y
CONFIG_SND_SEQ_MIDI_EMUL=y
CONFIG_SND_SEQ_VIRMIDI=y
CONFIG_SND_MPU401_UART=y
CONFIG_SND_OPL3_LIB=y
CONFIG_SND_OPL3_LIB_SEQ=y
CONFIG_SND_VX_LIB=y
CONFIG_SND_AC97_CODEC=y
CONFIG_SND_DRIVERS=y
CONFIG_SND_DUMMY=y
CONFIG_SND_ALOOP=y
CONFIG_SND_VIRMIDI=y
CONFIG_SND_MTPAV=y
CONFIG_SND_MTS64=y
CONFIG_SND_SERIAL_U16550=y
CONFIG_SND_SERIAL_GENERIC=y
CONFIG_SND_MPU401=y
CONFIG_SND_PORTMAN2X4=y
CONFIG_SND_AC97_POWER_SAVE=y
CONFIG_SND_AC97_POWER_SAVE_DEFAULT=0
CONFIG_SND_PCI=y
CONFIG_SND_AD1889=y
CONFIG_SND_ATIIXP=y
CONFIG_SND_ATIIXP_MODEM=y
CONFIG_SND_AU8810=y
CONFIG_SND_AU8820=y
CONFIG_SND_AU8830=y
CONFIG_SND_AW2=y
CONFIG_SND_BT87X=y
CONFIG_SND_BT87X_OVERCLOCK=y
CONFIG_SND_CA0106=y
CONFIG_SND_CMIPCI=y
CONFIG_SND_OXYGEN_LIB=y
CONFIG_SND_OXYGEN=y
CONFIG_SND_CS4281=y
CONFIG_SND_CS46XX=y
CONFIG_SND_CS46XX_NEW_DSP=y
CONFIG_SND_CS5535AUDIO=y
CONFIG_SND_CTXFI=y
CONFIG_SND_DARLA20=y
CONFIG_SND_GINA20=y
CONFIG_SND_LAYLA20=y
CONFIG_SND_DARLA24=y
CONFIG_SND_GINA24=y
CONFIG_SND_LAYLA24=y
CONFIG_SND_MONA=y
CONFIG_SND_MIA=y
CONFIG_SND_ECHO3G=y
CONFIG_SND_INDIGO=y
CONFIG_SND_INDIGOIO=y
CONFIG_SND_INDIGODJ=y
CONFIG_SND_INDIGOIOX=y
CONFIG_SND_INDIGODJX=y
CONFIG_SND_ENS1370=y
CONFIG_SND_ENS1371=y
CONFIG_SND_FM801=y
CONFIG_SND_FM801_TEA575X_BOOL=y
CONFIG_SND_HDSP=y

#
# Don't forget to add built-in firmwares for HDSP driver
#
CONFIG_SND_HDSPM=y
CONFIG_SND_ICE1724=y
CONFIG_SND_INTEL8X0=y
CONFIG_SND_INTEL8X0M=y
CONFIG_SND_KORG1212=y
CONFIG_SND_LOLA=y
CONFIG_SND_LX6464ES=y
CONFIG_SND_MIXART=y
CONFIG_SND_NM256=y
CONFIG_SND_PCXHR=y
CONFIG_SND_RIPTIDE=y
CONFIG_SND_RME32=y
CONFIG_SND_RME96=y
CONFIG_SND_RME9652=y
CONFIG_SND_VIA82XX=y
CONFIG_SND_VIA82XX_MODEM=y
CONFIG_SND_VIRTUOSO=y
CONFIG_SND_VX222=y
CONFIG_SND_YMFPCI=y

#
# HD-Audio
#
CONFIG_SND_HDA=y
CONFIG_SND_HDA_GENERIC_LEDS=y
CONFIG_SND_HDA_INTEL=y
CONFIG_SND_HDA_HWDEP=y
CONFIG_SND_HDA_RECONFIG=y
CONFIG_SND_HDA_INPUT_BEEP=y
CONFIG_SND_HDA_INPUT_BEEP_MODE=1
CONFIG_SND_HDA_PATCH_LOADER=y
CONFIG_SND_HDA_CODEC_REALTEK=y
CONFIG_SND_HDA_CODEC_ANALOG=y
CONFIG_SND_HDA_CODEC_SIGMATEL=y
CONFIG_SND_HDA_CODEC_VIA=y
CONFIG_SND_HDA_CODEC_HDMI=y
CONFIG_SND_HDA_CODEC_CIRRUS=y
CONFIG_SND_HDA_CODEC_CS8409=y
CONFIG_SND_HDA_CODEC_CONEXANT=y
CONFIG_SND_HDA_CODEC_CA0110=y
CONFIG_SND_HDA_CODEC_CA0132=y
CONFIG_SND_HDA_CODEC_CA0132_DSP=y
CONFIG_SND_HDA_CODEC_CMEDIA=y
CONFIG_SND_HDA_CODEC_SI3054=y
CONFIG_SND_HDA_GENERIC=y
CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0
CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM=y
# end of HD-Audio

CONFIG_SND_HDA_CORE=y
CONFIG_SND_HDA_DSP_LOADER=y
CONFIG_SND_HDA_COMPONENT=y
CONFIG_SND_HDA_EXT_CORE=y
CONFIG_SND_HDA_PREALLOC_SIZE=64
CONFIG_SND_INTEL_DSP_CONFIG=y
CONFIG_SND_PXA2XX_LIB=y
CONFIG_SND_SPI=y
CONFIG_SND_AT73C213=y
CONFIG_SND_AT73C213_TARGET_BITRATE=48000
CONFIG_SND_USB=y
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER=y
CONFIG_SND_USB_UA101=y
CONFIG_SND_USB_CAIAQ=y
CONFIG_SND_USB_CAIAQ_INPUT=y
CONFIG_SND_USB_US122L=y
CONFIG_SND_USB_6FIRE=y
CONFIG_SND_USB_HIFACE=y
CONFIG_SND_BCD2000=y
CONFIG_SND_USB_LINE6=y
CONFIG_SND_USB_POD=y
CONFIG_SND_USB_PODHD=y
CONFIG_SND_USB_TONEPORT=y
CONFIG_SND_USB_VARIAX=y
CONFIG_SND_FIREWIRE=y
CONFIG_SND_FIREWIRE_LIB=y
CONFIG_SND_DICE=y
CONFIG_SND_OXFW=y
CONFIG_SND_ISIGHT=y
CONFIG_SND_FIREWORKS=y
CONFIG_SND_BEBOB=y
CONFIG_SND_FIREWIRE_DIGI00X=y
CONFIG_SND_FIREWIRE_TASCAM=y
CONFIG_SND_FIREWIRE_MOTU=y
CONFIG_SND_FIREFACE=y
CONFIG_SND_PCMCIA=y
CONFIG_SND_VXPOCKET=y
CONFIG_SND_PDAUDIOCF=y
CONFIG_SND_SPARC=y
CONFIG_SND_SUN_AMD7930=y
CONFIG_SND_SUN_CS4231=y
CONFIG_SND_SUN_DBRI=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_AC97_BUS=y
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y
CONFIG_SND_SOC_COMPRESS=y
CONFIG_SND_SOC_TOPOLOGY=y
CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST=y
CONFIG_SND_SOC_UTILS_KUNIT_TEST=y
CONFIG_SND_SOC_ADI=y
CONFIG_SND_SOC_ADI_AXI_I2S=y
CONFIG_SND_SOC_ADI_AXI_SPDIF=y
CONFIG_SND_SOC_AMD_ACP=y
CONFIG_SND_SOC_AMD_CZ_RT5645_MACH=y
CONFIG_SND_AMD_ACP_CONFIG=y
CONFIG_SND_SOC_APPLE_MCA=y
CONFIG_SND_ATMEL_SOC=y
CONFIG_SND_ATMEL_SOC_PDC=y
CONFIG_SND_ATMEL_SOC_DMA=y
CONFIG_SND_ATMEL_SOC_SSC=y
CONFIG_SND_ATMEL_SOC_SSC_PDC=y
CONFIG_SND_ATMEL_SOC_SSC_DMA=y
CONFIG_SND_AT91_SOC_SAM9G20_WM8731=y
CONFIG_SND_ATMEL_SOC_WM8904=y
CONFIG_SND_AT91_SOC_SAM9X5_WM8731=y
CONFIG_SND_ATMEL_SOC_CLASSD=y
CONFIG_SND_ATMEL_SOC_PDMIC=y
CONFIG_SND_ATMEL_SOC_I2S=y
CONFIG_SND_SOC_MIKROE_PROTO=y
CONFIG_SND_MCHP_SOC_I2S_MCC=y
CONFIG_SND_MCHP_SOC_SPDIFTX=y
CONFIG_SND_MCHP_SOC_SPDIFRX=y
CONFIG_SND_MCHP_SOC_PDMC=y
CONFIG_SND_BCM2835_SOC_I2S=y
CONFIG_SND_SOC_CYGNUS=y
CONFIG_SND_BCM63XX_I2S_WHISTLER=y
CONFIG_SND_EP93XX_SOC=y
CONFIG_SND_DESIGNWARE_I2S=y
CONFIG_SND_DESIGNWARE_PCM=y

#
# SoC Audio for Freescale CPUs
#

#
# Common SoC Audio options for Freescale CPUs:
#
CONFIG_SND_SOC_FSL_ASRC=y
CONFIG_SND_SOC_FSL_SAI=y
CONFIG_SND_SOC_FSL_MQS=y
CONFIG_SND_SOC_FSL_AUDMIX=y
CONFIG_SND_SOC_FSL_SSI=y
CONFIG_SND_SOC_FSL_SPDIF=y
CONFIG_SND_SOC_FSL_ESAI=y
CONFIG_SND_SOC_FSL_MICFIL=y
CONFIG_SND_SOC_FSL_EASRC=y
CONFIG_SND_SOC_FSL_XCVR=y
CONFIG_SND_SOC_FSL_AUD2HTX=y
CONFIG_SND_SOC_FSL_UTILS=y
CONFIG_SND_SOC_FSL_RPMSG=y
CONFIG_SND_SOC_IMX_PCM_DMA=y
CONFIG_SND_SOC_IMX_AUDIO_RPMSG=y
CONFIG_SND_SOC_IMX_PCM_RPMSG=y
CONFIG_SND_SOC_IMX_AUDMUX=y
CONFIG_SND_IMX_SOC=y

#
# SoC Audio support for Freescale i.MX boards:
#
CONFIG_SND_SOC_IMX_ES8328=y
CONFIG_SND_SOC_IMX_SGTL5000=y
CONFIG_SND_SOC_IMX_SPDIF=y
CONFIG_SND_SOC_FSL_ASOC_CARD=y
CONFIG_SND_SOC_IMX_AUDMIX=y
CONFIG_SND_SOC_IMX_HDMI=y
CONFIG_SND_SOC_IMX_RPMSG=y
CONFIG_SND_SOC_IMX_CARD=y
# end of SoC Audio for Freescale CPUs

CONFIG_SND_I2S_HI6210_I2S=y
CONFIG_SND_JZ4740_SOC_I2S=y
CONFIG_SND_KIRKWOOD_SOC=y
CONFIG_SND_KIRKWOOD_SOC_ARMADA370_DB=y
CONFIG_SND_SOC_IMG=y
CONFIG_SND_SOC_IMG_I2S_IN=y
CONFIG_SND_SOC_IMG_I2S_OUT=y
CONFIG_SND_SOC_IMG_PARALLEL_OUT=y
CONFIG_SND_SOC_IMG_SPDIF_IN=y
CONFIG_SND_SOC_IMG_SPDIF_OUT=y
CONFIG_SND_SOC_IMG_PISTACHIO_INTERNAL_DAC=y
CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y
CONFIG_SND_SOC_ACPI_INTEL_MATCH=y
CONFIG_SND_SOC_INTEL_KEEMBAY=y
CONFIG_SND_SOC_INTEL_AVS=y

#
# Intel AVS Machine drivers
#

#
# Available DSP configurations
#
CONFIG_SND_SOC_INTEL_AVS_MACH_DA7219=y
CONFIG_SND_SOC_INTEL_AVS_MACH_DMIC=y
CONFIG_SND_SOC_INTEL_AVS_MACH_HDAUDIO=y
CONFIG_SND_SOC_INTEL_AVS_MACH_I2S_TEST=y
CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A=y
CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373=y
CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825=y
CONFIG_SND_SOC_INTEL_AVS_MACH_RT274=y
CONFIG_SND_SOC_INTEL_AVS_MACH_RT286=y
CONFIG_SND_SOC_INTEL_AVS_MACH_RT298=y
CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682=y
CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567=y
# end of Intel AVS Machine drivers

CONFIG_SND_SOC_INTEL_MACH=y
CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES=y
CONFIG_SND_SOC_INTEL_BDW_RT5650_MACH=y
CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH=y
CONFIG_SND_SOC_INTEL_BROADWELL_MACH=y
CONFIG_SND_SOC_MEDIATEK=y
CONFIG_SND_SOC_MT8186=y
CONFIG_SND_SOC_MT8186_MT6366_DA7219_MAX98357=y
CONFIG_SND_SOC_MT8186_MT6366_RT1019_RT5682S=y
CONFIG_SND_SOC_MTK_BTCVSD=y
CONFIG_SND_SOC_MT8195=y
CONFIG_SND_SOC_MT8195_MT6359=y

#
# ASoC support for Amlogic platforms
#
CONFIG_SND_MESON_AIU=y
CONFIG_SND_MESON_AXG_FIFO=y
CONFIG_SND_MESON_AXG_FRDDR=y
CONFIG_SND_MESON_AXG_TODDR=y
CONFIG_SND_MESON_AXG_TDM_FORMATTER=y
CONFIG_SND_MESON_AXG_TDM_INTERFACE=y
CONFIG_SND_MESON_AXG_TDMIN=y
CONFIG_SND_MESON_AXG_TDMOUT=y
CONFIG_SND_MESON_AXG_SOUND_CARD=y
CONFIG_SND_MESON_AXG_SPDIFOUT=y
CONFIG_SND_MESON_AXG_SPDIFIN=y
CONFIG_SND_MESON_AXG_PDM=y
CONFIG_SND_MESON_CARD_UTILS=y
CONFIG_SND_MESON_CODEC_GLUE=y
CONFIG_SND_MESON_GX_SOUND_CARD=y
CONFIG_SND_MESON_G12A_TOACODEC=y
CONFIG_SND_MESON_G12A_TOHDMITX=y
CONFIG_SND_SOC_MESON_T9015=y
# end of ASoC support for Amlogic platforms

CONFIG_SND_MXS_SOC=y
CONFIG_SND_SOC_MXS_SGTL5000=y
CONFIG_SND_PXA2XX_SOC=y
CONFIG_SND_SOC_QCOM=y
CONFIG_SND_SOC_LPASS_CPU=y
CONFIG_SND_SOC_LPASS_HDMI=y
CONFIG_SND_SOC_LPASS_PLATFORM=y
CONFIG_SND_SOC_LPASS_CDC_DMA=y
CONFIG_SND_SOC_LPASS_IPQ806X=y
CONFIG_SND_SOC_LPASS_APQ8016=y
CONFIG_SND_SOC_LPASS_SC7180=y
CONFIG_SND_SOC_LPASS_SC7280=y
CONFIG_SND_SOC_STORM=y
CONFIG_SND_SOC_APQ8016_SBC=y
CONFIG_SND_SOC_QCOM_COMMON=y
CONFIG_SND_SOC_QDSP6_COMMON=y
CONFIG_SND_SOC_QDSP6_CORE=y
CONFIG_SND_SOC_QDSP6_AFE=y
CONFIG_SND_SOC_QDSP6_AFE_DAI=y
CONFIG_SND_SOC_QDSP6_AFE_CLOCKS=y
CONFIG_SND_SOC_QDSP6_ADM=y
CONFIG_SND_SOC_QDSP6_ROUTING=y
CONFIG_SND_SOC_QDSP6_ASM=y
CONFIG_SND_SOC_QDSP6_ASM_DAI=y
CONFIG_SND_SOC_QDSP6_APM_DAI=y
CONFIG_SND_SOC_QDSP6_APM_LPASS_DAI=y
CONFIG_SND_SOC_QDSP6_APM=y
CONFIG_SND_SOC_QDSP6_PRM_LPASS_CLOCKS=y
CONFIG_SND_SOC_QDSP6_PRM=y
CONFIG_SND_SOC_QDSP6=y
CONFIG_SND_SOC_MSM8996=y
CONFIG_SND_SOC_SDM845=y
CONFIG_SND_SOC_SM8250=y
CONFIG_SND_SOC_SC8280XP=y
CONFIG_SND_SOC_SC7180=y
CONFIG_SND_SOC_SC7280=y
CONFIG_SND_SOC_ROCKCHIP=y
CONFIG_SND_SOC_ROCKCHIP_I2S=y
CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y
CONFIG_SND_SOC_ROCKCHIP_PDM=y
CONFIG_SND_SOC_ROCKCHIP_SPDIF=y
CONFIG_SND_SOC_ROCKCHIP_MAX98090=y
CONFIG_SND_SOC_ROCKCHIP_RT5645=y
CONFIG_SND_SOC_RK3288_HDMI_ANALOG=y
CONFIG_SND_SOC_RK3399_GRU_SOUND=y
CONFIG_SND_SOC_SAMSUNG=y
CONFIG_SND_S3C24XX_I2S=y
CONFIG_SND_SAMSUNG_PCM=y
CONFIG_SND_SAMSUNG_SPDIF=y
CONFIG_SND_SAMSUNG_I2S=y
CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753=y
CONFIG_SND_SOC_SAMSUNG_SMDK_WM8580=y
CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994=y
CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X=y
CONFIG_SND_SOC_SAMSUNG_SIMTEC=y
CONFIG_SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23=y
CONFIG_SND_SOC_SAMSUNG_SIMTEC_HERMES=y
CONFIG_SND_SOC_SAMSUNG_H1940_UDA1380=y
CONFIG_SND_SOC_SAMSUNG_RX1950_UDA1380=y
CONFIG_SND_SOC_SMARTQ=y
CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF=y
CONFIG_SND_SOC_SMDK_WM8994_PCM=y
CONFIG_SND_SOC_SPEYSIDE=y
CONFIG_SND_SOC_TOBERMORY=y
CONFIG_SND_SOC_BELLS=y
CONFIG_SND_SOC_LOWLAND=y
CONFIG_SND_SOC_LITTLEMILL=y
CONFIG_SND_SOC_SNOW=y
CONFIG_SND_SOC_ODROID=y
CONFIG_SND_SOC_ARNDALE=y
CONFIG_SND_SOC_SAMSUNG_TM2_WM5110=y
CONFIG_SND_SOC_SAMSUNG_ARIES_WM8994=y
CONFIG_SND_SOC_SAMSUNG_MIDAS_WM1811=y

#
# SoC Audio support for Renesas SoCs
#
CONFIG_SND_SOC_SH4_FSI=y
CONFIG_SND_SOC_RCAR=y
CONFIG_SND_SOC_RZ=y
# end of SoC Audio support for Renesas SoCs

CONFIG_SND_SOC_SOF_TOPLEVEL=y
CONFIG_SND_SOC_SOF_PCI_DEV=y
CONFIG_SND_SOC_SOF_PCI=y
CONFIG_SND_SOC_SOF_ACPI=y
CONFIG_SND_SOC_SOF_ACPI_DEV=y
CONFIG_SND_SOC_SOF_OF=y
CONFIG_SND_SOC_SOF_OF_DEV=y
CONFIG_SND_SOC_SOF_COMPRESS=y
CONFIG_SND_SOC_SOF_DEBUG_PROBES=y
CONFIG_SND_SOC_SOF_CLIENT=y
CONFIG_SND_SOC_SOF_DEVELOPER_SUPPORT=y
CONFIG_SND_SOC_SOF_FORCE_PROBE_WORKQUEUE=y
CONFIG_SND_SOC_SOF_NOCODEC=y
CONFIG_SND_SOC_SOF_NOCODEC_SUPPORT=y
CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS=y
CONFIG_SND_SOC_SOF_DEBUG=y
CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE=y
CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP=y
CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC=y
CONFIG_SND_SOC_SOF_DEBUG_FORCE_IPC_POSITION=y
CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE=y
CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE=y
CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST=y
CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM=2
CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR=y
CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT=y
CONFIG_SND_SOC_SOF=y
CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y
CONFIG_SND_SOC_SOF_IPC3=y
CONFIG_SND_SOC_SOF_INTEL_IPC4=y
CONFIG_SND_SOC_SOF_AMD_TOPLEVEL=y
CONFIG_SND_SOC_SOF_AMD_COMMON=y
CONFIG_SND_SOC_SOF_AMD_RENOIR=y
CONFIG_SND_SOC_SOF_AMD_REMBRANDT=y
CONFIG_SND_SOC_SOF_IMX_TOPLEVEL=y
CONFIG_SND_SOC_SOF_IMX_COMMON=y
CONFIG_SND_SOC_SOF_IMX8=y
CONFIG_SND_SOC_SOF_IMX8M=y
CONFIG_SND_SOC_SOF_IMX8ULP=y
CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL=y
CONFIG_SND_SOC_SOF_INTEL_HIFI_EP_IPC=y
CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP=y
CONFIG_SND_SOC_SOF_INTEL_COMMON=y
CONFIG_SND_SOC_SOF_BAYTRAIL=y
CONFIG_SND_SOC_SOF_BROADWELL=y
CONFIG_SND_SOC_SOF_MERRIFIELD=y
CONFIG_SND_SOC_SOF_INTEL_SKL=y
CONFIG_SND_SOC_SOF_SKYLAKE=y
CONFIG_SND_SOC_SOF_KABYLAKE=y
CONFIG_SND_SOC_SOF_INTEL_APL=y
CONFIG_SND_SOC_SOF_APOLLOLAKE=y
CONFIG_SND_SOC_SOF_GEMINILAKE=y
CONFIG_SND_SOC_SOF_INTEL_CNL=y
CONFIG_SND_SOC_SOF_CANNONLAKE=y
CONFIG_SND_SOC_SOF_COFFEELAKE=y
CONFIG_SND_SOC_SOF_COMETLAKE=y
CONFIG_SND_SOC_SOF_INTEL_ICL=y
CONFIG_SND_SOC_SOF_ICELAKE=y
CONFIG_SND_SOC_SOF_JASPERLAKE=y
CONFIG_SND_SOC_SOF_INTEL_TGL=y
CONFIG_SND_SOC_SOF_TIGERLAKE=y
CONFIG_SND_SOC_SOF_ELKHARTLAKE=y
CONFIG_SND_SOC_SOF_ALDERLAKE=y
CONFIG_SND_SOC_SOF_INTEL_MTL=y
CONFIG_SND_SOC_SOF_METEORLAKE=y
CONFIG_SND_SOC_SOF_HDA_COMMON=y
CONFIG_SND_SOC_SOF_HDA_LINK_BASELINE=y
CONFIG_SND_SOC_SOF_HDA_PROBES=y
CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE=y
CONFIG_SND_SOC_SOF_MTK_TOPLEVEL=y
CONFIG_SND_SOC_SOF_MTK_COMMON=y
CONFIG_SND_SOC_SOF_MT8186=y
CONFIG_SND_SOC_SOF_MT8195=y
CONFIG_SND_SOC_SOF_XTENSA=y
CONFIG_SND_SOC_SPRD=y
CONFIG_SND_SOC_SPRD_MCDT=y
CONFIG_SND_SOC_STI=y

#
# STMicroelectronics STM32 SOC audio support
#
CONFIG_SND_SOC_STM32_SAI=y
CONFIG_SND_SOC_STM32_I2S=y
CONFIG_SND_SOC_STM32_SPDIFRX=y
CONFIG_SND_SOC_STM32_DFSDM=y
# end of STMicroelectronics STM32 SOC audio support

#
# Allwinner SoC Audio support
#
CONFIG_SND_SUN4I_CODEC=y
CONFIG_SND_SUN8I_CODEC=y
CONFIG_SND_SUN8I_CODEC_ANALOG=y
CONFIG_SND_SUN50I_CODEC_ANALOG=y
CONFIG_SND_SUN4I_I2S=y
CONFIG_SND_SUN4I_SPDIF=y
CONFIG_SND_SUN50I_DMIC=y
CONFIG_SND_SUN8I_ADDA_PR_REGMAP=y
# end of Allwinner SoC Audio support

CONFIG_SND_SOC_TEGRA=y
CONFIG_SND_SOC_TEGRA20_AC97=y
CONFIG_SND_SOC_TEGRA20_DAS=y
CONFIG_SND_SOC_TEGRA20_I2S=y
CONFIG_SND_SOC_TEGRA20_SPDIF=y
CONFIG_SND_SOC_TEGRA30_AHUB=y
CONFIG_SND_SOC_TEGRA30_I2S=y
CONFIG_SND_SOC_TEGRA210_AHUB=y
CONFIG_SND_SOC_TEGRA210_DMIC=y
CONFIG_SND_SOC_TEGRA210_I2S=y
CONFIG_SND_SOC_TEGRA210_OPE=y
CONFIG_SND_SOC_TEGRA186_ASRC=y
CONFIG_SND_SOC_TEGRA186_DSPK=y
CONFIG_SND_SOC_TEGRA210_ADMAIF=y
CONFIG_SND_SOC_TEGRA210_MVC=y
CONFIG_SND_SOC_TEGRA210_SFC=y
CONFIG_SND_SOC_TEGRA210_AMX=y
CONFIG_SND_SOC_TEGRA210_ADX=y
CONFIG_SND_SOC_TEGRA210_MIXER=y
CONFIG_SND_SOC_TEGRA_AUDIO_GRAPH_CARD=y
CONFIG_SND_SOC_TEGRA_MACHINE_DRV=y
CONFIG_SND_SOC_TEGRA_RT5640=y
CONFIG_SND_SOC_TEGRA_WM8753=y
CONFIG_SND_SOC_TEGRA_WM8903=y
CONFIG_SND_SOC_TEGRA_WM9712=y
CONFIG_SND_SOC_TEGRA_TRIMSLICE=y
CONFIG_SND_SOC_TEGRA_ALC5632=y
CONFIG_SND_SOC_TEGRA_MAX98090=y
CONFIG_SND_SOC_TEGRA_RT5677=y
CONFIG_SND_SOC_TEGRA_SGTL5000=y

#
# Audio support for Texas Instruments SoCs
#
CONFIG_SND_SOC_TI_EDMA_PCM=y
CONFIG_SND_SOC_TI_SDMA_PCM=y
CONFIG_SND_SOC_TI_UDMA_PCM=y

#
# Texas Instruments DAI support for:
#
CONFIG_SND_SOC_DAVINCI_ASP=y
CONFIG_SND_SOC_DAVINCI_MCASP=y
CONFIG_SND_SOC_DAVINCI_VCIF=y
CONFIG_SND_SOC_OMAP_DMIC=y
CONFIG_SND_SOC_OMAP_MCBSP=y
CONFIG_SND_SOC_OMAP_MCPDM=y

#
# Audio support for boards with Texas Instruments SoCs
#
CONFIG_SND_SOC_OMAP3_TWL4030=y
CONFIG_SND_SOC_OMAP_ABE_TWL6040=y
CONFIG_SND_SOC_OMAP_HDMI=y
CONFIG_SND_SOC_J721E_EVM=y
# end of Audio support for Texas Instruments SoCs

CONFIG_SND_SOC_UNIPHIER=y
CONFIG_SND_SOC_UNIPHIER_AIO=y
CONFIG_SND_SOC_UNIPHIER_LD11=y
CONFIG_SND_SOC_UNIPHIER_PXS2=y
CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC=y
CONFIG_SND_SOC_XILINX_I2S=y
CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER=y
CONFIG_SND_SOC_XILINX_SPDIF=y
CONFIG_SND_SOC_XTFPGA_I2S=y
CONFIG_SND_SOC_I2C_AND_SPI=y

#
# CODEC drivers
#
CONFIG_SND_SOC_ALL_CODECS=y
CONFIG_SND_SOC_88PM860X=y
CONFIG_SND_SOC_ARIZONA=y
CONFIG_SND_SOC_WM_HUBS=y
CONFIG_SND_SOC_WM_ADSP=y
CONFIG_SND_SOC_AB8500_CODEC=y
CONFIG_SND_SOC_AC97_CODEC=y
CONFIG_SND_SOC_AD1836=y
CONFIG_SND_SOC_AD193X=y
CONFIG_SND_SOC_AD193X_SPI=y
CONFIG_SND_SOC_AD193X_I2C=y
CONFIG_SND_SOC_AD1980=y
CONFIG_SND_SOC_AD73311=y
CONFIG_SND_SOC_ADAU_UTILS=y
CONFIG_SND_SOC_ADAU1372=y
CONFIG_SND_SOC_ADAU1372_I2C=y
CONFIG_SND_SOC_ADAU1372_SPI=y
CONFIG_SND_SOC_ADAU1373=y
CONFIG_SND_SOC_ADAU1701=y
CONFIG_SND_SOC_ADAU17X1=y
CONFIG_SND_SOC_ADAU1761=y
CONFIG_SND_SOC_ADAU1761_I2C=y
CONFIG_SND_SOC_ADAU1761_SPI=y
CONFIG_SND_SOC_ADAU1781=y
CONFIG_SND_SOC_ADAU1781_I2C=y
CONFIG_SND_SOC_ADAU1781_SPI=y
CONFIG_SND_SOC_ADAU1977=y
CONFIG_SND_SOC_ADAU1977_SPI=y
CONFIG_SND_SOC_ADAU1977_I2C=y
CONFIG_SND_SOC_ADAU7002=y
CONFIG_SND_SOC_ADAU7118=y
CONFIG_SND_SOC_ADAU7118_HW=y
CONFIG_SND_SOC_ADAU7118_I2C=y
CONFIG_SND_SOC_ADAV80X=y
CONFIG_SND_SOC_ADAV801=y
CONFIG_SND_SOC_ADAV803=y
CONFIG_SND_SOC_ADS117X=y
CONFIG_SND_SOC_AK4104=y
CONFIG_SND_SOC_AK4118=y
CONFIG_SND_SOC_AK4375=y
CONFIG_SND_SOC_AK4458=y
CONFIG_SND_SOC_AK4535=y
CONFIG_SND_SOC_AK4554=y
CONFIG_SND_SOC_AK4613=y
CONFIG_SND_SOC_AK4641=y
CONFIG_SND_SOC_AK4642=y
CONFIG_SND_SOC_AK4671=y
CONFIG_SND_SOC_AK5386=y
CONFIG_SND_SOC_AK5558=y
CONFIG_SND_SOC_ALC5623=y
CONFIG_SND_SOC_ALC5632=y
CONFIG_SND_SOC_AW8738=y
CONFIG_SND_SOC_BD28623=y
CONFIG_SND_SOC_BT_SCO=y
CONFIG_SND_SOC_CPCAP=y
CONFIG_SND_SOC_CQ0093VC=y
CONFIG_SND_SOC_CROS_EC_CODEC=y
CONFIG_SND_SOC_CS35L32=y
CONFIG_SND_SOC_CS35L33=y
CONFIG_SND_SOC_CS35L34=y
CONFIG_SND_SOC_CS35L35=y
CONFIG_SND_SOC_CS35L36=y
CONFIG_SND_SOC_CS35L41_LIB=y
CONFIG_SND_SOC_CS35L41=y
CONFIG_SND_SOC_CS35L41_SPI=y
CONFIG_SND_SOC_CS35L41_I2C=y
CONFIG_SND_SOC_CS35L45_TABLES=y
CONFIG_SND_SOC_CS35L45=y
CONFIG_SND_SOC_CS35L45_SPI=y
CONFIG_SND_SOC_CS35L45_I2C=y
CONFIG_SND_SOC_CS42L42_CORE=y
CONFIG_SND_SOC_CS42L42=y
CONFIG_SND_SOC_CS42L51=y
CONFIG_SND_SOC_CS42L51_I2C=y
CONFIG_SND_SOC_CS42L52=y
CONFIG_SND_SOC_CS42L56=y
CONFIG_SND_SOC_CS42L73=y
CONFIG_SND_SOC_CS42L83=y
CONFIG_SND_SOC_CS4234=y
CONFIG_SND_SOC_CS4265=y
CONFIG_SND_SOC_CS4270=y
CONFIG_SND_SOC_CS4271=y
CONFIG_SND_SOC_CS4271_I2C=y
CONFIG_SND_SOC_CS4271_SPI=y
CONFIG_SND_SOC_CS42XX8=y
CONFIG_SND_SOC_CS42XX8_I2C=y
CONFIG_SND_SOC_CS43130=y
CONFIG_SND_SOC_CS4341=y
CONFIG_SND_SOC_CS4349=y
CONFIG_SND_SOC_CS47L15=y
CONFIG_SND_SOC_CS47L24=y
CONFIG_SND_SOC_CS47L35=y
CONFIG_SND_SOC_CS47L85=y
CONFIG_SND_SOC_CS47L90=y
CONFIG_SND_SOC_CS47L92=y
CONFIG_SND_SOC_CS53L30=y
CONFIG_SND_SOC_CX20442=y
CONFIG_SND_SOC_CX2072X=y
CONFIG_SND_SOC_JZ4740_CODEC=y
CONFIG_SND_SOC_JZ4725B_CODEC=y
CONFIG_SND_SOC_JZ4760_CODEC=y
CONFIG_SND_SOC_JZ4770_CODEC=y
CONFIG_SND_SOC_L3=y
CONFIG_SND_SOC_DA7210=y
CONFIG_SND_SOC_DA7213=y
CONFIG_SND_SOC_DA7218=y
CONFIG_SND_SOC_DA7219=y
CONFIG_SND_SOC_DA732X=y
CONFIG_SND_SOC_DA9055=y
CONFIG_SND_SOC_DMIC=y
CONFIG_SND_SOC_HDMI_CODEC=y
CONFIG_SND_SOC_ES7134=y
CONFIG_SND_SOC_ES7241=y
CONFIG_SND_SOC_ES8316=y
CONFIG_SND_SOC_ES8326=y
CONFIG_SND_SOC_ES8328=y
CONFIG_SND_SOC_ES8328_I2C=y
CONFIG_SND_SOC_ES8328_SPI=y
CONFIG_SND_SOC_GTM601=y
CONFIG_SND_SOC_HDAC_HDMI=y
CONFIG_SND_SOC_HDAC_HDA=y
CONFIG_SND_SOC_HDA=y
CONFIG_SND_SOC_ICS43432=y
CONFIG_SND_SOC_INNO_RK3036=y
CONFIG_SND_SOC_ISABELLE=y
CONFIG_SND_SOC_LM49453=y
CONFIG_SND_SOC_LOCHNAGAR_SC=y
CONFIG_SND_SOC_MADERA=y
CONFIG_SND_SOC_MAX98088=y
CONFIG_SND_SOC_MAX98090=y
CONFIG_SND_SOC_MAX98095=y
CONFIG_SND_SOC_MAX98357A=y
CONFIG_SND_SOC_MAX98371=y
CONFIG_SND_SOC_MAX98504=y
CONFIG_SND_SOC_MAX9867=y
CONFIG_SND_SOC_MAX98925=y
CONFIG_SND_SOC_MAX98926=y
CONFIG_SND_SOC_MAX98927=y
CONFIG_SND_SOC_MAX98520=y
CONFIG_SND_SOC_MAX98373=y
CONFIG_SND_SOC_MAX98373_I2C=y
CONFIG_SND_SOC_MAX98373_SDW=y
CONFIG_SND_SOC_MAX98390=y
CONFIG_SND_SOC_MAX98396=y
CONFIG_SND_SOC_MAX9850=y
CONFIG_SND_SOC_MAX9860=y
CONFIG_SND_SOC_MSM8916_WCD_ANALOG=y
CONFIG_SND_SOC_MSM8916_WCD_DIGITAL=y
CONFIG_SND_SOC_PCM1681=y
CONFIG_SND_SOC_PCM1789=y
CONFIG_SND_SOC_PCM1789_I2C=y
CONFIG_SND_SOC_PCM179X=y
CONFIG_SND_SOC_PCM179X_I2C=y
CONFIG_SND_SOC_PCM179X_SPI=y
CONFIG_SND_SOC_PCM186X=y
CONFIG_SND_SOC_PCM186X_I2C=y
CONFIG_SND_SOC_PCM186X_SPI=y
CONFIG_SND_SOC_PCM3008=y
CONFIG_SND_SOC_PCM3060=y
CONFIG_SND_SOC_PCM3060_I2C=y
CONFIG_SND_SOC_PCM3060_SPI=y
CONFIG_SND_SOC_PCM3168A=y
CONFIG_SND_SOC_PCM3168A_I2C=y
CONFIG_SND_SOC_PCM3168A_SPI=y
CONFIG_SND_SOC_PCM5102A=y
CONFIG_SND_SOC_PCM512x=y
CONFIG_SND_SOC_PCM512x_I2C=y
CONFIG_SND_SOC_PCM512x_SPI=y
CONFIG_SND_SOC_RK3328=y
CONFIG_SND_SOC_RK817=y
CONFIG_SND_SOC_RL6231=y
CONFIG_SND_SOC_RL6347A=y
CONFIG_SND_SOC_RT274=y
CONFIG_SND_SOC_RT286=y
CONFIG_SND_SOC_RT298=y
CONFIG_SND_SOC_RT1011=y
CONFIG_SND_SOC_RT1015=y
CONFIG_SND_SOC_RT1015P=y
CONFIG_SND_SOC_RT1016=y
CONFIG_SND_SOC_RT1019=y
CONFIG_SND_SOC_RT1305=y
CONFIG_SND_SOC_RT1308=y
CONFIG_SND_SOC_RT1308_SDW=y
CONFIG_SND_SOC_RT1316_SDW=y
CONFIG_SND_SOC_RT5514=y
CONFIG_SND_SOC_RT5514_SPI=y
CONFIG_SND_SOC_RT5616=y
CONFIG_SND_SOC_RT5631=y
CONFIG_SND_SOC_RT5640=y
CONFIG_SND_SOC_RT5645=y
CONFIG_SND_SOC_RT5651=y
CONFIG_SND_SOC_RT5659=y
CONFIG_SND_SOC_RT5660=y
CONFIG_SND_SOC_RT5663=y
CONFIG_SND_SOC_RT5665=y
CONFIG_SND_SOC_RT5668=y
CONFIG_SND_SOC_RT5670=y
CONFIG_SND_SOC_RT5677=y
CONFIG_SND_SOC_RT5677_SPI=y
CONFIG_SND_SOC_RT5682=y
CONFIG_SND_SOC_RT5682_I2C=y
CONFIG_SND_SOC_RT5682_SDW=y
CONFIG_SND_SOC_RT5682S=y
CONFIG_SND_SOC_RT700=y
CONFIG_SND_SOC_RT700_SDW=y
CONFIG_SND_SOC_RT711=y
CONFIG_SND_SOC_RT711_SDW=y
CONFIG_SND_SOC_RT711_SDCA_SDW=y
CONFIG_SND_SOC_RT715=y
CONFIG_SND_SOC_RT715_SDW=y
CONFIG_SND_SOC_RT715_SDCA_SDW=y
CONFIG_SND_SOC_RT9120=y
CONFIG_SND_SOC_SDW_MOCKUP=y
CONFIG_SND_SOC_SGTL5000=y
CONFIG_SND_SOC_SI476X=y
CONFIG_SND_SOC_SIGMADSP=y
CONFIG_SND_SOC_SIGMADSP_I2C=y
CONFIG_SND_SOC_SIGMADSP_REGMAP=y
CONFIG_SND_SOC_SIMPLE_AMPLIFIER=y
CONFIG_SND_SOC_SIMPLE_MUX=y
CONFIG_SND_SOC_SPDIF=y
CONFIG_SND_SOC_SRC4XXX_I2C=y
CONFIG_SND_SOC_SRC4XXX=y
CONFIG_SND_SOC_SSM2305=y
CONFIG_SND_SOC_SSM2518=y
CONFIG_SND_SOC_SSM2602=y
CONFIG_SND_SOC_SSM2602_SPI=y
CONFIG_SND_SOC_SSM2602_I2C=y
CONFIG_SND_SOC_SSM4567=y
CONFIG_SND_SOC_STA32X=y
CONFIG_SND_SOC_STA350=y
CONFIG_SND_SOC_STA529=y
CONFIG_SND_SOC_STAC9766=y
CONFIG_SND_SOC_STI_SAS=y
CONFIG_SND_SOC_TAS2552=y
CONFIG_SND_SOC_TAS2562=y
CONFIG_SND_SOC_TAS2764=y
CONFIG_SND_SOC_TAS2770=y
CONFIG_SND_SOC_TAS2780=y
CONFIG_SND_SOC_TAS5086=y
CONFIG_SND_SOC_TAS571X=y
CONFIG_SND_SOC_TAS5720=y
CONFIG_SND_SOC_TAS5805M=y
CONFIG_SND_SOC_TAS6424=y
CONFIG_SND_SOC_TDA7419=y
CONFIG_SND_SOC_TFA9879=y
CONFIG_SND_SOC_TFA989X=y
CONFIG_SND_SOC_TLV320ADC3XXX=y
CONFIG_SND_SOC_TLV320AIC23=y
CONFIG_SND_SOC_TLV320AIC23_I2C=y
CONFIG_SND_SOC_TLV320AIC23_SPI=y
CONFIG_SND_SOC_TLV320AIC26=y
CONFIG_SND_SOC_TLV320AIC31XX=y
CONFIG_SND_SOC_TLV320AIC32X4=y
CONFIG_SND_SOC_TLV320AIC32X4_I2C=y
CONFIG_SND_SOC_TLV320AIC32X4_SPI=y
CONFIG_SND_SOC_TLV320AIC3X=y
CONFIG_SND_SOC_TLV320AIC3X_I2C=y
CONFIG_SND_SOC_TLV320AIC3X_SPI=y
CONFIG_SND_SOC_TLV320DAC33=y
CONFIG_SND_SOC_TLV320ADCX140=y
CONFIG_SND_SOC_TS3A227E=y
CONFIG_SND_SOC_TSCS42XX=y
CONFIG_SND_SOC_TSCS454=y
CONFIG_SND_SOC_TWL4030=y
CONFIG_SND_SOC_TWL6040=y
CONFIG_SND_SOC_UDA1334=y
CONFIG_SND_SOC_UDA134X=y
CONFIG_SND_SOC_UDA1380=y
CONFIG_SND_SOC_WCD9335=y
CONFIG_SND_SOC_WCD_MBHC=y
CONFIG_SND_SOC_WCD934X=y
CONFIG_SND_SOC_WCD938X=y
CONFIG_SND_SOC_WCD938X_SDW=y
CONFIG_SND_SOC_WL1273=y
CONFIG_SND_SOC_WM0010=y
CONFIG_SND_SOC_WM1250_EV1=y
CONFIG_SND_SOC_WM2000=y
CONFIG_SND_SOC_WM2200=y
CONFIG_SND_SOC_WM5100=y
CONFIG_SND_SOC_WM5102=y
CONFIG_SND_SOC_WM5110=y
CONFIG_SND_SOC_WM8350=y
CONFIG_SND_SOC_WM8400=y
CONFIG_SND_SOC_WM8510=y
CONFIG_SND_SOC_WM8523=y
CONFIG_SND_SOC_WM8524=y
CONFIG_SND_SOC_WM8580=y
CONFIG_SND_SOC_WM8711=y
CONFIG_SND_SOC_WM8727=y
CONFIG_SND_SOC_WM8728=y
CONFIG_SND_SOC_WM8731=y
CONFIG_SND_SOC_WM8731_I2C=y
CONFIG_SND_SOC_WM8731_SPI=y
CONFIG_SND_SOC_WM8737=y
CONFIG_SND_SOC_WM8741=y
CONFIG_SND_SOC_WM8750=y
CONFIG_SND_SOC_WM8753=y
CONFIG_SND_SOC_WM8770=y
CONFIG_SND_SOC_WM8776=y
CONFIG_SND_SOC_WM8782=y
CONFIG_SND_SOC_WM8804=y
CONFIG_SND_SOC_WM8804_I2C=y
CONFIG_SND_SOC_WM8804_SPI=y
CONFIG_SND_SOC_WM8900=y
CONFIG_SND_SOC_WM8903=y
CONFIG_SND_SOC_WM8904=y
CONFIG_SND_SOC_WM8940=y
CONFIG_SND_SOC_WM8955=y
CONFIG_SND_SOC_WM8960=y
CONFIG_SND_SOC_WM8961=y
CONFIG_SND_SOC_WM8962=y
CONFIG_SND_SOC_WM8971=y
CONFIG_SND_SOC_WM8974=y
CONFIG_SND_SOC_WM8978=y
CONFIG_SND_SOC_WM8983=y
CONFIG_SND_SOC_WM8985=y
CONFIG_SND_SOC_WM8988=y
CONFIG_SND_SOC_WM8990=y
CONFIG_SND_SOC_WM8991=y
CONFIG_SND_SOC_WM8993=y
CONFIG_SND_SOC_WM8994=y
CONFIG_SND_SOC_WM8995=y
CONFIG_SND_SOC_WM8996=y
CONFIG_SND_SOC_WM8997=y
CONFIG_SND_SOC_WM8998=y
CONFIG_SND_SOC_WM9081=y
CONFIG_SND_SOC_WM9090=y
CONFIG_SND_SOC_WM9705=y
CONFIG_SND_SOC_WM9712=y
CONFIG_SND_SOC_WM9713=y
CONFIG_SND_SOC_WSA881X=y
CONFIG_SND_SOC_WSA883X=y
CONFIG_SND_SOC_ZL38060=y
CONFIG_SND_SOC_LM4857=y
CONFIG_SND_SOC_MAX9759=y
CONFIG_SND_SOC_MAX9768=y
CONFIG_SND_SOC_MAX9877=y
CONFIG_SND_SOC_MC13783=y
CONFIG_SND_SOC_ML26124=y
CONFIG_SND_SOC_MT6351=y
CONFIG_SND_SOC_MT6358=y
CONFIG_SND_SOC_MT6359=y
CONFIG_SND_SOC_MT6359_ACCDET=y
CONFIG_SND_SOC_MT6660=y
CONFIG_SND_SOC_NAU8315=y
CONFIG_SND_SOC_NAU8540=y
CONFIG_SND_SOC_NAU8810=y
CONFIG_SND_SOC_NAU8821=y
CONFIG_SND_SOC_NAU8822=y
CONFIG_SND_SOC_NAU8824=y
CONFIG_SND_SOC_NAU8825=y
CONFIG_SND_SOC_TPA6130A2=y
CONFIG_SND_SOC_LPASS_MACRO_COMMON=y
CONFIG_SND_SOC_LPASS_WSA_MACRO=y
CONFIG_SND_SOC_LPASS_VA_MACRO=y
CONFIG_SND_SOC_LPASS_RX_MACRO=y
CONFIG_SND_SOC_LPASS_TX_MACRO=y
# end of CODEC drivers

CONFIG_SND_SIMPLE_CARD_UTILS=y
CONFIG_SND_SIMPLE_CARD=y
CONFIG_SND_AUDIO_GRAPH_CARD=y
CONFIG_SND_AUDIO_GRAPH_CARD2=y
CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE=y
CONFIG_SND_TEST_COMPONENT=y
CONFIG_SND_VIRTIO=y
CONFIG_AC97_BUS=y

#
# HID support
#
CONFIG_HID=y
CONFIG_HID_BATTERY_STRENGTH=y
CONFIG_HIDRAW=y
CONFIG_UHID=y
CONFIG_HID_GENERIC=y

#
# Special HID drivers
#
CONFIG_HID_A4TECH=y
CONFIG_HID_ACCUTOUCH=y
CONFIG_HID_ACRUX=y
CONFIG_HID_ACRUX_FF=y
CONFIG_HID_APPLE=y
CONFIG_HID_APPLEIR=y
CONFIG_HID_ASUS=y
CONFIG_HID_AUREAL=y
CONFIG_HID_BELKIN=y
CONFIG_HID_BETOP_FF=y
CONFIG_HID_BIGBEN_FF=y
CONFIG_HID_CHERRY=y
CONFIG_HID_CHICONY=y
CONFIG_HID_CORSAIR=y
CONFIG_HID_COUGAR=y
CONFIG_HID_MACALLY=y
CONFIG_HID_PRODIKEYS=y
CONFIG_HID_CMEDIA=y
CONFIG_HID_CP2112=y
CONFIG_HID_CREATIVE_SB0540=y
CONFIG_HID_CYPRESS=y
CONFIG_HID_DRAGONRISE=y
CONFIG_DRAGONRISE_FF=y
CONFIG_HID_EMS_FF=y
CONFIG_HID_ELAN=y
CONFIG_HID_ELECOM=y
CONFIG_HID_ELO=y
CONFIG_HID_EZKEY=y
CONFIG_HID_FT260=y
CONFIG_HID_GEMBIRD=y
CONFIG_HID_GFRM=y
CONFIG_HID_GLORIOUS=y
CONFIG_HID_HOLTEK=y
CONFIG_HOLTEK_FF=y
CONFIG_HID_VIVALDI_COMMON=y
CONFIG_HID_GOOGLE_HAMMER=y
CONFIG_HID_VIVALDI=y
CONFIG_HID_GT683R=y
CONFIG_HID_KEYTOUCH=y
CONFIG_HID_KYE=y
CONFIG_HID_UCLOGIC=y
CONFIG_HID_WALTOP=y
CONFIG_HID_VIEWSONIC=y
CONFIG_HID_VRC2=y
CONFIG_HID_XIAOMI=y
CONFIG_HID_GYRATION=y
CONFIG_HID_ICADE=y
CONFIG_HID_ITE=y
CONFIG_HID_JABRA=y
CONFIG_HID_TWINHAN=y
CONFIG_HID_KENSINGTON=y
CONFIG_HID_LCPOWER=y
CONFIG_HID_LED=y
CONFIG_HID_LENOVO=y
CONFIG_HID_LETSKETCH=y
CONFIG_HID_LOGITECH=y
CONFIG_HID_LOGITECH_DJ=y
CONFIG_HID_LOGITECH_HIDPP=y
CONFIG_LOGITECH_FF=y
CONFIG_LOGIRUMBLEPAD2_FF=y
CONFIG_LOGIG940_FF=y
CONFIG_LOGIWHEELS_FF=y
CONFIG_HID_MAGICMOUSE=y
CONFIG_HID_MALTRON=y
CONFIG_HID_MAYFLASH=y
CONFIG_HID_MEGAWORLD_FF=y
CONFIG_HID_REDRAGON=y
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MONTEREY=y
CONFIG_HID_MULTITOUCH=y
CONFIG_HID_NINTENDO=y
CONFIG_NINTENDO_FF=y
CONFIG_HID_NTI=y
CONFIG_HID_NTRIG=y
CONFIG_HID_ORTEK=y
CONFIG_HID_PANTHERLORD=y
CONFIG_PANTHERLORD_FF=y
CONFIG_HID_PENMOUNT=y
CONFIG_HID_PETALYNX=y
CONFIG_HID_PICOLCD=y
CONFIG_HID_PICOLCD_FB=y
CONFIG_HID_PICOLCD_BACKLIGHT=y
CONFIG_HID_PICOLCD_LCD=y
CONFIG_HID_PICOLCD_LEDS=y
CONFIG_HID_PICOLCD_CIR=y
CONFIG_HID_PLANTRONICS=y
CONFIG_HID_PLAYSTATION=y
CONFIG_PLAYSTATION_FF=y
CONFIG_HID_PXRC=y
CONFIG_HID_RAZER=y
CONFIG_HID_PRIMAX=y
CONFIG_HID_RETRODE=y
CONFIG_HID_ROCCAT=y
CONFIG_HID_SAITEK=y
CONFIG_HID_SAMSUNG=y
CONFIG_HID_SEMITEK=y
CONFIG_HID_SIGMAMICRO=y
CONFIG_HID_SONY=y
CONFIG_SONY_FF=y
CONFIG_HID_SPEEDLINK=y
CONFIG_HID_STEAM=y
CONFIG_HID_STEELSERIES=y
CONFIG_HID_SUNPLUS=y
CONFIG_HID_RMI=y
CONFIG_HID_GREENASIA=y
CONFIG_GREENASIA_FF=y
CONFIG_HID_SMARTJOYPLUS=y
CONFIG_SMARTJOYPLUS_FF=y
CONFIG_HID_TIVO=y
CONFIG_HID_TOPSEED=y
CONFIG_HID_TOPRE=y
CONFIG_HID_THINGM=y
CONFIG_HID_THRUSTMASTER=y
CONFIG_THRUSTMASTER_FF=y
CONFIG_HID_UDRAW_PS3=y
CONFIG_HID_U2FZERO=y
CONFIG_HID_WACOM=y
CONFIG_HID_WIIMOTE=y
CONFIG_HID_XINMO=y
CONFIG_HID_ZEROPLUS=y
CONFIG_ZEROPLUS_FF=y
CONFIG_HID_ZYDACRON=y
CONFIG_HID_SENSOR_HUB=y
CONFIG_HID_SENSOR_CUSTOM_SENSOR=y
CONFIG_HID_ALPS=y
CONFIG_HID_MCP2221=y
CONFIG_HID_KUNIT_TEST=y
# end of Special HID drivers

#
# USB HID support
#
CONFIG_USB_HID=y
CONFIG_HID_PID=y
CONFIG_USB_HIDDEV=y
# end of USB HID support

#
# I2C HID support
#
CONFIG_I2C_HID_OF=y
CONFIG_I2C_HID_OF_ELAN=y
CONFIG_I2C_HID_OF_GOODIX=y
# end of I2C HID support

CONFIG_I2C_HID_CORE=y

#
# Intel ISH HID support
#
# end of Intel ISH HID support

#
# AMD SFH HID Support
#
CONFIG_AMD_SFH_HID=y
# end of AMD SFH HID Support
# end of HID support

CONFIG_USB_OHCI_LITTLE_ENDIAN=y
CONFIG_USB_SUPPORT=y
CONFIG_USB_COMMON=y
CONFIG_USB_LED_TRIG=y
CONFIG_USB_ULPI_BUS=y
CONFIG_USB_CONN_GPIO=y
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB=y
CONFIG_USB_PCI=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y

#
# Miscellaneous USB options
#
CONFIG_USB_DEFAULT_PERSIST=y
CONFIG_USB_FEW_INIT_RETRIES=y
CONFIG_USB_DYNAMIC_MINORS=y
CONFIG_USB_OTG=y
CONFIG_USB_OTG_PRODUCTLIST=y
CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB=y
CONFIG_USB_OTG_FSM=y
CONFIG_USB_LEDS_TRIGGER_USBPORT=y
CONFIG_USB_AUTOSUSPEND_DELAY=2
CONFIG_USB_MON=y

#
# USB Host Controller Drivers
#
CONFIG_USB_C67X00_HCD=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_DBGCAP=y
CONFIG_USB_XHCI_PCI=y
CONFIG_USB_XHCI_PCI_RENESAS=y
CONFIG_USB_XHCI_PLATFORM=y
CONFIG_USB_XHCI_HISTB=y
CONFIG_USB_XHCI_MTK=y
CONFIG_USB_XHCI_MVEBU=y
CONFIG_USB_XHCI_RCAR=y
CONFIG_USB_EHCI_BRCMSTB=y
CONFIG_USB_BRCMSTB=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_ROOT_HUB_TT=y
CONFIG_USB_EHCI_TT_NEWSCHED=y
CONFIG_USB_EHCI_PCI=y
CONFIG_USB_EHCI_FSL=y
CONFIG_USB_EHCI_HCD_NPCM7XX=y
CONFIG_USB_EHCI_HCD_OMAP=y
CONFIG_USB_EHCI_HCD_ORION=y
CONFIG_USB_EHCI_HCD_SPEAR=y
CONFIG_USB_EHCI_HCD_STI=y
CONFIG_USB_EHCI_HCD_AT91=y
CONFIG_USB_EHCI_SH=y
CONFIG_USB_EHCI_EXYNOS=y
CONFIG_USB_EHCI_MV=y
CONFIG_USB_CNS3XXX_EHCI=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OXU210HP_HCD=y
CONFIG_USB_ISP116X_HCD=y
CONFIG_USB_ISP1362_HCD=y
CONFIG_USB_FOTG210_HCD=y
CONFIG_USB_MAX3421_HCD=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_SPEAR=y
CONFIG_USB_OHCI_HCD_STI=y
CONFIG_USB_OHCI_HCD_S3C2410=y
CONFIG_USB_OHCI_HCD_LPC32XX=y
CONFIG_USB_OHCI_HCD_AT91=y
CONFIG_USB_OHCI_HCD_OMAP3=y
CONFIG_USB_OHCI_HCD_DAVINCI=y
CONFIG_USB_OHCI_HCD_PCI=y
CONFIG_USB_OHCI_HCD_SSB=y
CONFIG_USB_OHCI_SH=y
CONFIG_USB_OHCI_EXYNOS=y
CONFIG_USB_CNS3XXX_OHCI=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_UHCI_HCD=y
CONFIG_USB_U132_HCD=y
CONFIG_USB_SL811_HCD=y
CONFIG_USB_SL811_HCD_ISO=y
CONFIG_USB_SL811_CS=y
CONFIG_USB_R8A66597_HCD=y
CONFIG_USB_RENESAS_USBHS_HCD=y
CONFIG_USB_HCD_BCMA=y
CONFIG_USB_HCD_SSB=y
CONFIG_USB_HCD_TEST_MODE=y
CONFIG_USB_RENESAS_USBHS=y

#
# USB Device Class drivers
#
CONFIG_USB_ACM=y
CONFIG_USB_PRINTER=y
CONFIG_USB_WDM=y
CONFIG_USB_TMC=y

#
# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
#

#
# also be needed; see USB_STORAGE Help for more info
#
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DEBUG=y
CONFIG_USB_STORAGE_REALTEK=y
CONFIG_REALTEK_AUTOPM=y
CONFIG_USB_STORAGE_DATAFAB=y
CONFIG_USB_STORAGE_FREECOM=y
CONFIG_USB_STORAGE_ISD200=y
CONFIG_USB_STORAGE_USBAT=y
CONFIG_USB_STORAGE_SDDR09=y
CONFIG_USB_STORAGE_SDDR55=y
CONFIG_USB_STORAGE_JUMPSHOT=y
CONFIG_USB_STORAGE_ALAUDA=y
CONFIG_USB_STORAGE_ONETOUCH=y
CONFIG_USB_STORAGE_KARMA=y
CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_STORAGE_ENE_UB6250=y
CONFIG_USB_UAS=y

#
# USB Imaging devices
#
CONFIG_USB_MDC800=y
CONFIG_USB_MICROTEK=y
CONFIG_USBIP_CORE=y
CONFIG_USBIP_VHCI_HCD=y
CONFIG_USBIP_VHCI_HC_PORTS=8
CONFIG_USBIP_VHCI_NR_HCS=1
CONFIG_USBIP_HOST=y
CONFIG_USBIP_VUDC=y
CONFIG_USBIP_DEBUG=y
CONFIG_USB_CDNS_SUPPORT=y
CONFIG_USB_CDNS_HOST=y
CONFIG_USB_CDNS3=y
CONFIG_USB_CDNS3_GADGET=y
CONFIG_USB_CDNS3_HOST=y
CONFIG_USB_CDNS3_TI=y
CONFIG_USB_CDNS3_IMX=y
CONFIG_USB_MTU3=y
# CONFIG_USB_MTU3_HOST is not set
# CONFIG_USB_MTU3_GADGET is not set
CONFIG_USB_MTU3_DUAL_ROLE=y
CONFIG_USB_MTU3_DEBUG=y
CONFIG_USB_MUSB_HDRC=y
# CONFIG_USB_MUSB_HOST is not set
# CONFIG_USB_MUSB_GADGET is not set
CONFIG_USB_MUSB_DUAL_ROLE=y

#
# Platform Glue Layer
#
CONFIG_USB_MUSB_TUSB6010=y
CONFIG_USB_MUSB_UX500=y
CONFIG_USB_MUSB_MEDIATEK=y
CONFIG_USB_MUSB_POLARFIRE_SOC=y

#
# MUSB DMA mode
#
CONFIG_MUSB_PIO_ONLY=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_ULPI=y
# CONFIG_USB_DWC3_HOST is not set
# CONFIG_USB_DWC3_GADGET is not set
CONFIG_USB_DWC3_DUAL_ROLE=y

#
# Platform Glue Driver Support
#
CONFIG_USB_DWC3_OMAP=y
CONFIG_USB_DWC3_EXYNOS=y
CONFIG_USB_DWC3_HAPS=y
CONFIG_USB_DWC3_KEYSTONE=y
CONFIG_USB_DWC3_MESON_G12A=y
CONFIG_USB_DWC3_OF_SIMPLE=y
CONFIG_USB_DWC3_ST=y
CONFIG_USB_DWC3_QCOM=y
CONFIG_USB_DWC3_IMX8MP=y
CONFIG_USB_DWC3_AM62=y
CONFIG_USB_DWC2=y
# CONFIG_USB_DWC2_HOST is not set

#
# Gadget/Dual-role mode requires USB Gadget support to be enabled
#
# CONFIG_USB_DWC2_PERIPHERAL is not set
CONFIG_USB_DWC2_DUAL_ROLE=y
CONFIG_USB_DWC2_PCI=y
CONFIG_USB_DWC2_DEBUG=y
CONFIG_USB_DWC2_VERBOSE=y
CONFIG_USB_DWC2_TRACK_MISSED_SOFS=y
CONFIG_USB_DWC2_DEBUG_PERIODIC=y
CONFIG_USB_CHIPIDEA=y
CONFIG_USB_CHIPIDEA_UDC=y
CONFIG_USB_CHIPIDEA_HOST=y
CONFIG_USB_CHIPIDEA_PCI=y
CONFIG_USB_CHIPIDEA_MSM=y
CONFIG_USB_CHIPIDEA_IMX=y
CONFIG_USB_CHIPIDEA_GENERIC=y
CONFIG_USB_CHIPIDEA_TEGRA=y
CONFIG_USB_ISP1760=y
CONFIG_USB_ISP1760_HCD=y
CONFIG_USB_ISP1761_UDC=y
# CONFIG_USB_ISP1760_HOST_ROLE is not set
# CONFIG_USB_ISP1760_GADGET_ROLE is not set
CONFIG_USB_ISP1760_DUAL_ROLE=y

#
# USB port drivers
#
CONFIG_USB_USS720=y
CONFIG_USB_SERIAL=y
CONFIG_USB_SERIAL_CONSOLE=y
CONFIG_USB_SERIAL_GENERIC=y
CONFIG_USB_SERIAL_SIMPLE=y
CONFIG_USB_SERIAL_AIRCABLE=y
CONFIG_USB_SERIAL_ARK3116=y
CONFIG_USB_SERIAL_BELKIN=y
CONFIG_USB_SERIAL_CH341=y
CONFIG_USB_SERIAL_WHITEHEAT=y
CONFIG_USB_SERIAL_DIGI_ACCELEPORT=y
CONFIG_USB_SERIAL_CP210X=y
CONFIG_USB_SERIAL_CYPRESS_M8=y
CONFIG_USB_SERIAL_EMPEG=y
CONFIG_USB_SERIAL_FTDI_SIO=y
CONFIG_USB_SERIAL_VISOR=y
CONFIG_USB_SERIAL_IPAQ=y
CONFIG_USB_SERIAL_IR=y
CONFIG_USB_SERIAL_EDGEPORT=y
CONFIG_USB_SERIAL_EDGEPORT_TI=y
CONFIG_USB_SERIAL_F81232=y
CONFIG_USB_SERIAL_F8153X=y
CONFIG_USB_SERIAL_GARMIN=y
CONFIG_USB_SERIAL_IPW=y
CONFIG_USB_SERIAL_IUU=y
CONFIG_USB_SERIAL_KEYSPAN_PDA=y
CONFIG_USB_SERIAL_KEYSPAN=y
CONFIG_USB_SERIAL_KLSI=y
CONFIG_USB_SERIAL_KOBIL_SCT=y
CONFIG_USB_SERIAL_MCT_U232=y
CONFIG_USB_SERIAL_METRO=y
CONFIG_USB_SERIAL_MOS7720=y
CONFIG_USB_SERIAL_MOS7715_PARPORT=y
CONFIG_USB_SERIAL_MOS7840=y
CONFIG_USB_SERIAL_MXUPORT=y
CONFIG_USB_SERIAL_NAVMAN=y
CONFIG_USB_SERIAL_PL2303=y
CONFIG_USB_SERIAL_OTI6858=y
CONFIG_USB_SERIAL_QCAUX=y
CONFIG_USB_SERIAL_QUALCOMM=y
CONFIG_USB_SERIAL_SPCP8X5=y
CONFIG_USB_SERIAL_SAFE=y
CONFIG_USB_SERIAL_SAFE_PADDED=y
CONFIG_USB_SERIAL_SIERRAWIRELESS=y
CONFIG_USB_SERIAL_SYMBOL=y
CONFIG_USB_SERIAL_TI=y
CONFIG_USB_SERIAL_CYBERJACK=y
CONFIG_USB_SERIAL_WWAN=y
CONFIG_USB_SERIAL_OPTION=y
CONFIG_USB_SERIAL_OMNINET=y
CONFIG_USB_SERIAL_OPTICON=y
CONFIG_USB_SERIAL_XSENS_MT=y
CONFIG_USB_SERIAL_WISHBONE=y
CONFIG_USB_SERIAL_SSU100=y
CONFIG_USB_SERIAL_QT2=y
CONFIG_USB_SERIAL_UPD78F0730=y
CONFIG_USB_SERIAL_XR=y
CONFIG_USB_SERIAL_DEBUG=y

#
# USB Miscellaneous drivers
#
CONFIG_USB_EMI62=y
CONFIG_USB_EMI26=y
CONFIG_USB_ADUTUX=y
CONFIG_USB_SEVSEG=y
CONFIG_USB_LEGOTOWER=y
CONFIG_USB_LCD=y
CONFIG_USB_CYPRESS_CY7C63=y
CONFIG_USB_CYTHERM=y
CONFIG_USB_IDMOUSE=y
CONFIG_USB_FTDI_ELAN=y
CONFIG_USB_APPLEDISPLAY=y
CONFIG_USB_QCOM_EUD=y
CONFIG_APPLE_MFI_FASTCHARGE=y
CONFIG_USB_SISUSBVGA=y
CONFIG_USB_LD=y
CONFIG_USB_TRANCEVIBRATOR=y
CONFIG_USB_IOWARRIOR=y
CONFIG_USB_TEST=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_ISIGHTFW=y
CONFIG_USB_YUREX=y
CONFIG_USB_EZUSB_FX2=y
CONFIG_USB_HUB_USB251XB=y
CONFIG_USB_HSIC_USB3503=y
CONFIG_USB_HSIC_USB4604=y
CONFIG_USB_LINK_LAYER_TEST=y
CONFIG_USB_CHAOSKEY=y
CONFIG_BRCM_USB_PINMAP=y
CONFIG_USB_ONBOARD_HUB=y
CONFIG_USB_ATM=y
CONFIG_USB_SPEEDTOUCH=y
CONFIG_USB_CXACRU=y
CONFIG_USB_UEAGLEATM=y
CONFIG_USB_XUSBATM=y

#
# USB Physical Layer drivers
#
CONFIG_USB_PHY=y
CONFIG_KEYSTONE_USB_PHY=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_AM335X_CONTROL_USB=y
CONFIG_AM335X_PHY_USB=y
# CONFIG_USB_GPIO_VBUS is not set
CONFIG_TAHVO_USB=y
CONFIG_TAHVO_USB_HOST_BY_DEFAULT=y
CONFIG_USB_ISP1301=y
CONFIG_USB_MV_OTG=y
CONFIG_USB_TEGRA_PHY=y
CONFIG_USB_ULPI=y
CONFIG_USB_ULPI_VIEWPORT=y
# CONFIG_JZ4770_PHY is not set
# end of USB Physical Layer drivers

CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG=y
CONFIG_USB_GADGET_VERBOSE=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_DEBUG_FS=y
CONFIG_USB_GADGET_VBUS_DRAW=2
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
CONFIG_U_SERIAL_CONSOLE=y

#
# USB Peripheral Controller
#
CONFIG_USB_LPC32XX=y
CONFIG_USB_FOTG210_UDC=y
CONFIG_USB_GR_UDC=y
CONFIG_USB_R8A66597=y
CONFIG_USB_RENESAS_USBHS_UDC=y
CONFIG_USB_RENESAS_USB3=y
CONFIG_USB_PXA27X=y
CONFIG_USB_MV_UDC=y
CONFIG_USB_MV_U3D=y
CONFIG_USB_SNP_CORE=y
CONFIG_USB_SNP_UDC_PLAT=y
CONFIG_USB_M66592=y
CONFIG_USB_BDC_UDC=y
CONFIG_USB_AMD5536UDC=y
CONFIG_USB_NET2272=y
CONFIG_USB_NET2272_DMA=y
CONFIG_USB_NET2280=y
CONFIG_USB_GOKU=y
CONFIG_USB_EG20T=y
CONFIG_USB_GADGET_XILINX=y
CONFIG_USB_MAX3420_UDC=y
CONFIG_USB_ASPEED_UDC=y
CONFIG_USB_ASPEED_VHUB=y
CONFIG_USB_DUMMY_HCD=y
# end of USB Peripheral Controller

CONFIG_USB_LIBCOMPOSITE=y
CONFIG_USB_F_ACM=y
CONFIG_USB_F_SS_LB=y
CONFIG_USB_U_SERIAL=y
CONFIG_USB_U_ETHER=y
CONFIG_USB_U_AUDIO=y
CONFIG_USB_F_SERIAL=y
CONFIG_USB_F_OBEX=y
CONFIG_USB_F_NCM=y
CONFIG_USB_F_ECM=y
CONFIG_USB_F_PHONET=y
CONFIG_USB_F_EEM=y
CONFIG_USB_F_SUBSET=y
CONFIG_USB_F_RNDIS=y
CONFIG_USB_F_MASS_STORAGE=y
CONFIG_USB_F_FS=y
CONFIG_USB_F_UAC1=y
CONFIG_USB_F_UAC1_LEGACY=y
CONFIG_USB_F_UAC2=y
CONFIG_USB_F_UVC=y
CONFIG_USB_F_MIDI=y
CONFIG_USB_F_HID=y
CONFIG_USB_F_PRINTER=y
CONFIG_USB_F_TCM=y
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_ACM=y
CONFIG_USB_CONFIGFS_OBEX=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
CONFIG_USB_CONFIGFS_ECM_SUBSET=y
CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_EEM=y
CONFIG_USB_CONFIGFS_PHONET=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_LB_SS=y
CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_CONFIGFS_F_UAC1=y
CONFIG_USB_CONFIGFS_F_UAC1_LEGACY=y
CONFIG_USB_CONFIGFS_F_UAC2=y
CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_UVC=y
CONFIG_USB_CONFIGFS_F_PRINTER=y
CONFIG_USB_CONFIGFS_F_TCM=y

#
# USB Gadget precomposed configurations
#
CONFIG_USB_ZERO=y
CONFIG_USB_ZERO_HNPTEST=y
CONFIG_USB_AUDIO=y
CONFIG_GADGET_UAC1=y
CONFIG_GADGET_UAC1_LEGACY=y
CONFIG_USB_ETH=y
CONFIG_USB_ETH_RNDIS=y
CONFIG_USB_ETH_EEM=y
CONFIG_USB_G_NCM=y
CONFIG_USB_GADGETFS=y
CONFIG_USB_FUNCTIONFS=y
CONFIG_USB_FUNCTIONFS_ETH=y
CONFIG_USB_FUNCTIONFS_RNDIS=y
CONFIG_USB_FUNCTIONFS_GENERIC=y
CONFIG_USB_MASS_STORAGE=y
CONFIG_USB_GADGET_TARGET=y
CONFIG_USB_G_SERIAL=y
CONFIG_USB_MIDI_GADGET=y
CONFIG_USB_G_PRINTER=y
CONFIG_USB_CDC_COMPOSITE=y
CONFIG_USB_G_NOKIA=y
CONFIG_USB_G_ACM_MS=y
CONFIG_USB_G_MULTI=y
CONFIG_USB_G_MULTI_RNDIS=y
CONFIG_USB_G_MULTI_CDC=y
CONFIG_USB_G_HID=y
CONFIG_USB_G_DBGP=y
# CONFIG_USB_G_DBGP_PRINTK is not set
CONFIG_USB_G_DBGP_SERIAL=y
CONFIG_USB_G_WEBCAM=y
CONFIG_USB_RAW_GADGET=y
# end of USB Gadget precomposed configurations

CONFIG_TYPEC=y
CONFIG_TYPEC_TCPM=y
CONFIG_TYPEC_TCPCI=y
CONFIG_TYPEC_RT1711H=y
CONFIG_TYPEC_MT6360=y
CONFIG_TYPEC_TCPCI_MT6370=y
CONFIG_TYPEC_TCPCI_MAXIM=y
CONFIG_TYPEC_FUSB302=y
CONFIG_TYPEC_TPS6598X=y
CONFIG_TYPEC_ANX7411=y
CONFIG_TYPEC_RT1719=y
CONFIG_TYPEC_HD3SS3220=y
CONFIG_TYPEC_STUSB160X=y
CONFIG_TYPEC_QCOM_PMIC=y
CONFIG_TYPEC_WUSB3801=y

#
# USB Type-C Multiplexer/DeMultiplexer Switch support
#
CONFIG_TYPEC_MUX_FSA4480=y
CONFIG_TYPEC_MUX_PI3USB30532=y
# end of USB Type-C Multiplexer/DeMultiplexer Switch support

#
# USB Type-C Alternate Mode drivers
#
CONFIG_TYPEC_DP_ALTMODE=y
CONFIG_TYPEC_NVIDIA_ALTMODE=y
# end of USB Type-C Alternate Mode drivers

CONFIG_USB_ROLE_SWITCH=y
CONFIG_MMC=y
CONFIG_PWRSEQ_EMMC=y
CONFIG_PWRSEQ_SD8787=y
CONFIG_PWRSEQ_SIMPLE=y
CONFIG_MMC_BLOCK=y
CONFIG_MMC_BLOCK_MINORS=8
CONFIG_SDIO_UART=y
CONFIG_MMC_TEST=y
CONFIG_MMC_CRYPTO=y

#
# MMC/SD/SDIO Host Controller Drivers
#
CONFIG_MMC_DEBUG=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_IO_ACCESSORS=y
CONFIG_MMC_SDHCI_PCI=y
CONFIG_MMC_RICOH_MMC=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_OF_ARASAN=y
CONFIG_MMC_SDHCI_OF_AT91=y
CONFIG_MMC_SDHCI_OF_ESDHC=y
CONFIG_MMC_SDHCI_OF_DWCMSHC=y
CONFIG_MMC_SDHCI_OF_SPARX5=y
CONFIG_MMC_SDHCI_CADENCE=y
CONFIG_MMC_SDHCI_CNS3XXX=y
CONFIG_MMC_SDHCI_ESDHC_IMX=y
CONFIG_MMC_SDHCI_DOVE=y
CONFIG_MMC_SDHCI_TEGRA=y
CONFIG_MMC_SDHCI_S3C=y
CONFIG_MMC_SDHCI_PXAV3=y
CONFIG_MMC_SDHCI_PXAV2=y
CONFIG_MMC_SDHCI_SPEAR=y
CONFIG_MMC_SDHCI_S3C_DMA=y
CONFIG_MMC_SDHCI_BCM_KONA=y
CONFIG_MMC_SDHCI_F_SDH30=y
CONFIG_MMC_SDHCI_MILBEAUT=y
CONFIG_MMC_SDHCI_IPROC=y
CONFIG_MMC_MESON_GX=y
CONFIG_MMC_MESON_MX_SDHC=y
CONFIG_MMC_MOXART=y
CONFIG_MMC_SDHCI_ST=y
CONFIG_MMC_OMAP_HS=y
CONFIG_MMC_ALCOR=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_TIFM_SD=y
CONFIG_MMC_DAVINCI=y
CONFIG_MMC_SPI=y
CONFIG_MMC_S3C=y
CONFIG_MMC_S3C_HW_SDIO_IRQ=y
CONFIG_MMC_S3C_PIO=y
# CONFIG_MMC_S3C_DMA is not set
CONFIG_MMC_SDRICOH_CS=y
CONFIG_MMC_SDHCI_SPRD=y
CONFIG_MMC_TMIO_CORE=y
CONFIG_MMC_TMIO=y
CONFIG_MMC_SDHI=y
CONFIG_MMC_SDHI_SYS_DMAC=y
CONFIG_MMC_SDHI_INTERNAL_DMAC=y
CONFIG_MMC_UNIPHIER=y
CONFIG_MMC_CB710=y
CONFIG_MMC_VIA_SDMMC=y
CONFIG_MMC_DW=y
CONFIG_MMC_DW_PLTFM=y
CONFIG_MMC_DW_BLUEFIELD=y
CONFIG_MMC_DW_EXYNOS=y
CONFIG_MMC_DW_HI3798CV200=y
CONFIG_MMC_DW_K3=y
CONFIG_MMC_DW_PCI=y
CONFIG_MMC_SH_MMCIF=y
CONFIG_MMC_VUB300=y
CONFIG_MMC_USHC=y
CONFIG_MMC_USDHI6ROL0=y
CONFIG_MMC_REALTEK_PCI=y
CONFIG_MMC_REALTEK_USB=y
CONFIG_MMC_SUNXI=y
CONFIG_MMC_CQHCI=y
CONFIG_MMC_HSQ=y
CONFIG_MMC_TOSHIBA_PCI=y
CONFIG_MMC_BCM2835=y
CONFIG_MMC_MTK=y
CONFIG_MMC_SDHCI_XENON=y
CONFIG_MMC_SDHCI_OMAP=y
CONFIG_MMC_SDHCI_AM654=y
CONFIG_MMC_OWL=y
CONFIG_MMC_SDHCI_EXTERNAL_DMA=y
CONFIG_MMC_LITEX=y
CONFIG_SCSI_UFSHCD=y
CONFIG_SCSI_UFS_BSG=y
CONFIG_SCSI_UFS_CRYPTO=y
CONFIG_SCSI_UFS_HPB=y
CONFIG_SCSI_UFS_FAULT_INJECTION=y
CONFIG_SCSI_UFS_HWMON=y
CONFIG_SCSI_UFSHCD_PCI=y
CONFIG_SCSI_UFS_DWC_TC_PCI=y
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_CDNS_PLATFORM=y
CONFIG_SCSI_UFS_DWC_TC_PLATFORM=y
CONFIG_SCSI_UFS_HISI=y
CONFIG_SCSI_UFS_RENESAS=y
CONFIG_SCSI_UFS_TI_J721E=y
CONFIG_SCSI_UFS_EXYNOS=y
CONFIG_MEMSTICK=y
CONFIG_MEMSTICK_DEBUG=y

#
# MemoryStick drivers
#
CONFIG_MEMSTICK_UNSAFE_RESUME=y
CONFIG_MSPRO_BLOCK=y
CONFIG_MS_BLOCK=y

#
# MemoryStick Host Controller Drivers
#
CONFIG_MEMSTICK_TIFM_MS=y
CONFIG_MEMSTICK_JMICRON_38X=y
CONFIG_MEMSTICK_R592=y
CONFIG_MEMSTICK_REALTEK_PCI=y
CONFIG_MEMSTICK_REALTEK_USB=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_CLASS_FLASH=y
CONFIG_LEDS_CLASS_MULTICOLOR=y
CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y

#
# LED drivers
#
CONFIG_LEDS_88PM860X=y
CONFIG_LEDS_AN30259A=y
CONFIG_LEDS_ARIEL=y
CONFIG_LEDS_AW2013=y
CONFIG_LEDS_BCM6328=y
CONFIG_LEDS_BCM6358=y
CONFIG_LEDS_CPCAP=y
CONFIG_LEDS_CR0014114=y
CONFIG_LEDS_EL15203000=y
CONFIG_LEDS_TURRIS_OMNIA=y
CONFIG_LEDS_LM3530=y
CONFIG_LEDS_LM3532=y
CONFIG_LEDS_LM3533=y
CONFIG_LEDS_LM3642=y
CONFIG_LEDS_LM3692X=y
CONFIG_LEDS_MT6323=y
CONFIG_LEDS_S3C24XX=y
CONFIG_LEDS_COBALT_QUBE=y
CONFIG_LEDS_COBALT_RAQ=y
CONFIG_LEDS_SUNFIRE=y
CONFIG_LEDS_PCA9532=y
CONFIG_LEDS_PCA9532_GPIO=y
CONFIG_LEDS_GPIO=y
CONFIG_LEDS_LP3944=y
CONFIG_LEDS_LP3952=y
CONFIG_LEDS_LP50XX=y
CONFIG_LEDS_LP55XX_COMMON=y
CONFIG_LEDS_LP5521=y
CONFIG_LEDS_LP5523=y
CONFIG_LEDS_LP5562=y
CONFIG_LEDS_LP8501=y
CONFIG_LEDS_LP8788=y
CONFIG_LEDS_LP8860=y
CONFIG_LEDS_PCA955X=y
CONFIG_LEDS_PCA955X_GPIO=y
CONFIG_LEDS_PCA963X=y
CONFIG_LEDS_WM831X_STATUS=y
CONFIG_LEDS_WM8350=y
CONFIG_LEDS_DA903X=y
CONFIG_LEDS_DA9052=y
CONFIG_LEDS_DAC124S085=y
CONFIG_LEDS_PWM=y
CONFIG_LEDS_REGULATOR=y
CONFIG_LEDS_BD2802=y
CONFIG_LEDS_LT3593=y
CONFIG_LEDS_ADP5520=y
CONFIG_LEDS_MC13783=y
CONFIG_LEDS_NS2=y
CONFIG_LEDS_NETXBIG=y
CONFIG_LEDS_ASIC3=y
CONFIG_LEDS_TCA6507=y
CONFIG_LEDS_TLC591XX=y
CONFIG_LEDS_MAX77650=y
CONFIG_LEDS_MAX8997=y
CONFIG_LEDS_LM355x=y
CONFIG_LEDS_OT200=y
CONFIG_LEDS_MENF21BMC=y
CONFIG_LEDS_IS31FL319X=y
CONFIG_LEDS_IS31FL32XX=y
CONFIG_LEDS_SC27XX_BLTC=y

#
# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)
#
CONFIG_LEDS_BLINKM=y
CONFIG_LEDS_SYSCON=y
CONFIG_LEDS_PM8058=y
CONFIG_LEDS_MLXREG=y
CONFIG_LEDS_USER=y
CONFIG_LEDS_SPI_BYTE=y
CONFIG_LEDS_TI_LMU_COMMON=y
CONFIG_LEDS_LM3697=y
CONFIG_LEDS_LM36274=y
CONFIG_LEDS_TPS6105X=y
CONFIG_LEDS_IP30=y
CONFIG_LEDS_ACER_A500=y
CONFIG_LEDS_BCM63138=y
CONFIG_LEDS_LGM=y

#
# Flash and Torch LED drivers
#
CONFIG_LEDS_AAT1290=y
CONFIG_LEDS_AS3645A=y
CONFIG_LEDS_KTD2692=y
CONFIG_LEDS_LM3601X=y
CONFIG_LEDS_MAX77693=y
CONFIG_LEDS_MT6360=y
CONFIG_LEDS_RT4505=y
CONFIG_LEDS_RT8515=y
CONFIG_LEDS_SGM3140=y

#
# RGB LED drivers
#
CONFIG_LEDS_PWM_MULTICOLOR=y
CONFIG_LEDS_QCOM_LPG=y

#
# LED Triggers
#
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_ONESHOT=y
CONFIG_LEDS_TRIGGER_DISK=y
CONFIG_LEDS_TRIGGER_MTD=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
CONFIG_LEDS_TRIGGER_CPU=y
CONFIG_LEDS_TRIGGER_ACTIVITY=y
CONFIG_LEDS_TRIGGER_GPIO=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y

#
# iptables trigger is under Netfilter config (LED target)
#
CONFIG_LEDS_TRIGGER_TRANSIENT=y
CONFIG_LEDS_TRIGGER_CAMERA=y
CONFIG_LEDS_TRIGGER_PANIC=y
CONFIG_LEDS_TRIGGER_NETDEV=y
CONFIG_LEDS_TRIGGER_PATTERN=y
CONFIG_LEDS_TRIGGER_AUDIO=y
CONFIG_LEDS_TRIGGER_TTY=y

#
# Simple LED drivers
#
CONFIG_ACCESSIBILITY=y
CONFIG_A11Y_BRAILLE_CONSOLE=y

#
# Speakup console speech
#
CONFIG_SPEAKUP=y
CONFIG_SPEAKUP_SERIALIO=y
CONFIG_SPEAKUP_SYNTH_ACNTSA=y
CONFIG_SPEAKUP_SYNTH_ACNTPC=y
CONFIG_SPEAKUP_SYNTH_APOLLO=y
CONFIG_SPEAKUP_SYNTH_AUDPTR=y
CONFIG_SPEAKUP_SYNTH_BNS=y
CONFIG_SPEAKUP_SYNTH_DECTLK=y
CONFIG_SPEAKUP_SYNTH_DECEXT=y
CONFIG_SPEAKUP_SYNTH_DECPC=m
CONFIG_SPEAKUP_SYNTH_DTLK=y
CONFIG_SPEAKUP_SYNTH_KEYPC=y
CONFIG_SPEAKUP_SYNTH_LTLK=y
CONFIG_SPEAKUP_SYNTH_SOFT=y
CONFIG_SPEAKUP_SYNTH_SPKOUT=y
CONFIG_SPEAKUP_SYNTH_TXPRT=y
CONFIG_SPEAKUP_SYNTH_DUMMY=y
# end of Speakup console speech

CONFIG_INFINIBAND=y
CONFIG_INFINIBAND_USER_MAD=y
CONFIG_INFINIBAND_USER_ACCESS=y
CONFIG_INFINIBAND_USER_MEM=y
CONFIG_INFINIBAND_ON_DEMAND_PAGING=y
CONFIG_INFINIBAND_ADDR_TRANS=y
CONFIG_INFINIBAND_ADDR_TRANS_CONFIGFS=y
CONFIG_INFINIBAND_VIRT_DMA=y
CONFIG_INFINIBAND_BNXT_RE=y
CONFIG_INFINIBAND_CXGB4=y
CONFIG_INFINIBAND_ERDMA=y
CONFIG_INFINIBAND_HNS=y
CONFIG_INFINIBAND_HNS_HIP08=y
CONFIG_INFINIBAND_IRDMA=y
CONFIG_MLX4_INFINIBAND=y
CONFIG_MLX5_INFINIBAND=y
CONFIG_INFINIBAND_MTHCA=y
CONFIG_INFINIBAND_MTHCA_DEBUG=y
CONFIG_INFINIBAND_OCRDMA=y
CONFIG_INFINIBAND_QEDR=y
CONFIG_INFINIBAND_VMWARE_PVRDMA=y
CONFIG_RDMA_RXE=y
CONFIG_RDMA_SIW=y
CONFIG_INFINIBAND_IPOIB=y
CONFIG_INFINIBAND_IPOIB_CM=y
CONFIG_INFINIBAND_IPOIB_DEBUG=y
CONFIG_INFINIBAND_IPOIB_DEBUG_DATA=y
CONFIG_INFINIBAND_SRP=y
CONFIG_INFINIBAND_SRPT=y
CONFIG_INFINIBAND_ISER=y
CONFIG_INFINIBAND_ISERT=y
CONFIG_INFINIBAND_RTRS=y
CONFIG_INFINIBAND_RTRS_CLIENT=y
CONFIG_INFINIBAND_RTRS_SERVER=y
CONFIG_RTC_LIB=y
CONFIG_RTC_MC146818_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
CONFIG_RTC_SYSTOHC=y
CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
CONFIG_RTC_DEBUG=y
CONFIG_RTC_LIB_KUNIT_TEST=y
CONFIG_RTC_NVMEM=y

#
# RTC interfaces
#
CONFIG_RTC_INTF_SYSFS=y
CONFIG_RTC_INTF_PROC=y
CONFIG_RTC_INTF_DEV=y
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
CONFIG_RTC_DRV_TEST=y

#
# I2C RTC drivers
#
CONFIG_RTC_DRV_88PM860X=y
CONFIG_RTC_DRV_88PM80X=y
CONFIG_RTC_DRV_ABB5ZES3=y
CONFIG_RTC_DRV_ABEOZ9=y
CONFIG_RTC_DRV_ABX80X=y
CONFIG_RTC_DRV_BRCMSTB=y
CONFIG_RTC_DRV_AS3722=y
CONFIG_RTC_DRV_DS1307=y
CONFIG_RTC_DRV_DS1307_CENTURY=y
CONFIG_RTC_DRV_DS1374=y
CONFIG_RTC_DRV_DS1374_WDT=y
CONFIG_RTC_DRV_DS1672=y
CONFIG_RTC_DRV_HYM8563=y
CONFIG_RTC_DRV_LP8788=y
CONFIG_RTC_DRV_MAX6900=y
CONFIG_RTC_DRV_MAX8907=y
CONFIG_RTC_DRV_MAX8925=y
CONFIG_RTC_DRV_MAX8998=y
CONFIG_RTC_DRV_MAX8997=y
CONFIG_RTC_DRV_MAX77686=y
CONFIG_RTC_DRV_NCT3018Y=y
CONFIG_RTC_DRV_RK808=y
CONFIG_RTC_DRV_RS5C372=y
CONFIG_RTC_DRV_ISL1208=y
CONFIG_RTC_DRV_ISL12022=y
CONFIG_RTC_DRV_ISL12026=y
CONFIG_RTC_DRV_X1205=y
CONFIG_RTC_DRV_PCF8523=y
CONFIG_RTC_DRV_PCF85063=y
CONFIG_RTC_DRV_PCF85363=y
CONFIG_RTC_DRV_PCF8563=y
CONFIG_RTC_DRV_PCF8583=y
CONFIG_RTC_DRV_M41T80=y
CONFIG_RTC_DRV_M41T80_WDT=y
CONFIG_RTC_DRV_BD70528=y
CONFIG_RTC_DRV_BQ32K=y
CONFIG_RTC_DRV_TWL4030=y
CONFIG_RTC_DRV_PALMAS=y
CONFIG_RTC_DRV_TPS6586X=y
CONFIG_RTC_DRV_TPS65910=y
CONFIG_RTC_DRV_RC5T583=y
CONFIG_RTC_DRV_RC5T619=y
CONFIG_RTC_DRV_S35390A=y
CONFIG_RTC_DRV_FM3130=y
CONFIG_RTC_DRV_RX8010=y
CONFIG_RTC_DRV_RX8581=y
CONFIG_RTC_DRV_RX8025=y
CONFIG_RTC_DRV_EM3027=y
CONFIG_RTC_DRV_RV3028=y
CONFIG_RTC_DRV_RV3032=y
CONFIG_RTC_DRV_RV8803=y
CONFIG_RTC_DRV_S5M=y
CONFIG_RTC_DRV_SD3078=y

#
# SPI RTC drivers
#
CONFIG_RTC_DRV_M41T93=y
CONFIG_RTC_DRV_M41T94=y
CONFIG_RTC_DRV_DS1302=y
CONFIG_RTC_DRV_DS1305=y
CONFIG_RTC_DRV_DS1343=y
CONFIG_RTC_DRV_DS1347=y
CONFIG_RTC_DRV_DS1390=y
CONFIG_RTC_DRV_MAX6916=y
CONFIG_RTC_DRV_R9701=y
CONFIG_RTC_DRV_RX4581=y
CONFIG_RTC_DRV_RS5C348=y
CONFIG_RTC_DRV_MAX6902=y
CONFIG_RTC_DRV_PCF2123=y
CONFIG_RTC_DRV_MCP795=y
CONFIG_RTC_I2C_AND_SPI=y

#
# SPI and I2C RTC drivers
#
CONFIG_RTC_DRV_DS3232=y
CONFIG_RTC_DRV_DS3232_HWMON=y
CONFIG_RTC_DRV_PCF2127=y
CONFIG_RTC_DRV_RV3029C2=y
CONFIG_RTC_DRV_RV3029_HWMON=y
CONFIG_RTC_DRV_RX6110=y

#
# Platform RTC drivers
#
CONFIG_RTC_DRV_CMOS=y
CONFIG_RTC_DRV_DS1286=y
CONFIG_RTC_DRV_DS1511=y
CONFIG_RTC_DRV_DS1553=y
CONFIG_RTC_DRV_DS1685_FAMILY=y
CONFIG_RTC_DRV_DS1685=y
# CONFIG_RTC_DRV_DS1689 is not set
# CONFIG_RTC_DRV_DS17285 is not set
# CONFIG_RTC_DRV_DS17485 is not set
# CONFIG_RTC_DRV_DS17885 is not set
CONFIG_RTC_DRV_DS1742=y
CONFIG_RTC_DRV_DS2404=y
CONFIG_RTC_DRV_DA9052=y
CONFIG_RTC_DRV_DA9055=y
CONFIG_RTC_DRV_DA9063=y
CONFIG_RTC_DRV_STK17TA8=y
CONFIG_RTC_DRV_M48T86=y
CONFIG_RTC_DRV_M48T35=y
CONFIG_RTC_DRV_M48T59=y
CONFIG_RTC_DRV_MSM6242=y
CONFIG_RTC_DRV_BQ4802=y
CONFIG_RTC_DRV_RP5C01=y
CONFIG_RTC_DRV_V3020=y
CONFIG_RTC_DRV_GAMECUBE=y
CONFIG_RTC_DRV_WM831X=y
CONFIG_RTC_DRV_WM8350=y
CONFIG_RTC_DRV_SC27XX=y
CONFIG_RTC_DRV_SPEAR=y
CONFIG_RTC_DRV_PCF50633=y
CONFIG_RTC_DRV_ZYNQMP=y
CONFIG_RTC_DRV_CROS_EC=y
CONFIG_RTC_DRV_NTXEC=y

#
# on-CPU RTC drivers
#
CONFIG_RTC_DRV_ASM9260=y
CONFIG_RTC_DRV_DAVINCI=y
CONFIG_RTC_DRV_DIGICOLOR=y
CONFIG_RTC_DRV_FSL_FTM_ALARM=y
CONFIG_RTC_DRV_MESON=y
CONFIG_RTC_DRV_MESON_VRTC=y
CONFIG_RTC_DRV_OMAP=y
CONFIG_RTC_DRV_S3C=y
CONFIG_RTC_DRV_EP93XX=y
CONFIG_RTC_DRV_AT91RM9200=y
CONFIG_RTC_DRV_AT91SAM9=y
CONFIG_RTC_DRV_RZN1=y
CONFIG_RTC_DRV_GENERIC=y
CONFIG_RTC_DRV_VT8500=y
CONFIG_RTC_DRV_SUN4V=y
CONFIG_RTC_DRV_SUN6I=y
CONFIG_RTC_DRV_SUNXI=y
CONFIG_RTC_DRV_STARFIRE=y
CONFIG_RTC_DRV_MV=y
CONFIG_RTC_DRV_ARMADA38X=y
CONFIG_RTC_DRV_CADENCE=y
CONFIG_RTC_DRV_FTRTC010=y
CONFIG_RTC_DRV_STMP=y
CONFIG_RTC_DRV_PCAP=y
CONFIG_RTC_DRV_MC13XXX=y
CONFIG_RTC_DRV_JZ4740=y
CONFIG_RTC_DRV_LPC24XX=y
CONFIG_RTC_DRV_LPC32XX=y
CONFIG_RTC_DRV_PM8XXX=y
CONFIG_RTC_DRV_TEGRA=y
CONFIG_RTC_DRV_MXC=y
CONFIG_RTC_DRV_MXC_V2=y
CONFIG_RTC_DRV_SNVS=y
CONFIG_RTC_DRV_MOXART=y
CONFIG_RTC_DRV_MT2712=y
CONFIG_RTC_DRV_MT6397=y
CONFIG_RTC_DRV_MT7622=y
CONFIG_RTC_DRV_XGENE=y
CONFIG_RTC_DRV_R7301=y
CONFIG_RTC_DRV_STM32=y
CONFIG_RTC_DRV_CPCAP=y
CONFIG_RTC_DRV_RTD119X=y
CONFIG_RTC_DRV_ASPEED=y
CONFIG_RTC_DRV_TI_K3=y

#
# HID Sensor RTC drivers
#
CONFIG_RTC_DRV_HID_SENSOR_TIME=y
CONFIG_RTC_DRV_GOLDFISH=y
CONFIG_RTC_DRV_MSC313=y
CONFIG_DMADEVICES=y
CONFIG_DMADEVICES_DEBUG=y
CONFIG_DMADEVICES_VDEBUG=y

#
# DMA Devices
#
CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH=y
CONFIG_DMA_ENGINE=y
CONFIG_DMA_VIRTUAL_CHANNELS=y
CONFIG_DMA_OF=y
CONFIG_ALTERA_MSGDMA=y
CONFIG_APPLE_ADMAC=y
CONFIG_AXI_DMAC=y
CONFIG_BCM_SBA_RAID=y
CONFIG_DMA_JZ4780=y
CONFIG_DMA_SA11X0=y
CONFIG_DMA_SUN6I=y
CONFIG_DW_AXI_DMAC=y
CONFIG_EP93XX_DMA=y
CONFIG_FSL_EDMA=y
CONFIG_HISI_DMA=y
CONFIG_IMG_MDC_DMA=y
CONFIG_INTEL_IDMA64=y
CONFIG_INTEL_IOP_ADMA=y
CONFIG_K3_DMA=y
CONFIG_MCF_EDMA=y
CONFIG_MILBEAUT_HDMAC=y
CONFIG_MILBEAUT_XDMAC=y
CONFIG_MMP_PDMA=y
CONFIG_MMP_TDMA=y
CONFIG_MV_XOR=y
CONFIG_MXS_DMA=y
CONFIG_NBPFAXI_DMA=y
CONFIG_PCH_DMA=y
CONFIG_PLX_DMA=y
CONFIG_STM32_DMA=y
CONFIG_STM32_DMAMUX=y
CONFIG_STM32_MDMA=y
CONFIG_SPRD_DMA=y
CONFIG_S3C24XX_DMAC=y
CONFIG_TEGRA186_GPC_DMA=y
CONFIG_TEGRA20_APB_DMA=y
CONFIG_TEGRA210_ADMA=y
CONFIG_TIMB_DMA=y
CONFIG_UNIPHIER_MDMAC=y
CONFIG_UNIPHIER_XDMAC=y
CONFIG_XGENE_DMA=y
CONFIG_XILINX_ZYNQMP_DMA=y
CONFIG_XILINX_ZYNQMP_DPDMA=y
CONFIG_MTK_HSDMA=y
CONFIG_MTK_CQDMA=y
CONFIG_MTK_UART_APDMA=y
CONFIG_QCOM_HIDMA_MGMT=y
CONFIG_QCOM_HIDMA=y
CONFIG_DW_DMAC_CORE=y
CONFIG_DW_DMAC=y
CONFIG_RZN1_DMAMUX=y
CONFIG_DW_DMAC_PCI=y
CONFIG_DW_EDMA=y
CONFIG_DW_EDMA_PCIE=y
CONFIG_HSU_DMA=y
CONFIG_SF_PDMA=y
CONFIG_RENESAS_DMA=y
CONFIG_SH_DMAE_BASE=y
CONFIG_SH_DMAE=y
CONFIG_RCAR_DMAC=y
CONFIG_RENESAS_USB_DMAC=y
CONFIG_RZ_DMAC=y
CONFIG_TI_EDMA=y
CONFIG_DMA_OMAP=y
CONFIG_TI_DMA_CROSSBAR=y
CONFIG_INTEL_LDMA=y

#
# DMA Clients
#
CONFIG_ASYNC_TX_DMA=y
CONFIG_DMATEST=y
CONFIG_DMA_ENGINE_RAID=y

#
# DMABUF options
#
CONFIG_SYNC_FILE=y
CONFIG_SW_SYNC=y
CONFIG_UDMABUF=y
CONFIG_DMABUF_MOVE_NOTIFY=y
CONFIG_DMABUF_DEBUG=y
CONFIG_DMABUF_SELFTESTS=y
CONFIG_DMABUF_HEAPS=y
CONFIG_DMABUF_SYSFS_STATS=y
CONFIG_DMABUF_HEAPS_SYSTEM=y
# end of DMABUF options

CONFIG_AUXDISPLAY=y
CONFIG_CHARLCD=y
CONFIG_LINEDISP=y
CONFIG_HD44780_COMMON=y
CONFIG_HD44780=y
CONFIG_KS0108=y
CONFIG_KS0108_PORT=0x378
CONFIG_KS0108_DELAY=2
CONFIG_IMG_ASCII_LCD=y
CONFIG_HT16K33=y
CONFIG_LCD2S=y
CONFIG_PARPORT_PANEL=y
CONFIG_PANEL_PARPORT=0
CONFIG_PANEL_PROFILE=5
CONFIG_PANEL_CHANGE_MESSAGE=y
CONFIG_PANEL_BOOT_MESSAGE=""
# CONFIG_CHARLCD_BL_OFF is not set
# CONFIG_CHARLCD_BL_ON is not set
CONFIG_CHARLCD_BL_FLASH=y
CONFIG_PANEL=y
CONFIG_UIO=y
CONFIG_UIO_CIF=y
CONFIG_UIO_PDRV_GENIRQ=y
CONFIG_UIO_DMEM_GENIRQ=y
CONFIG_UIO_AEC=y
CONFIG_UIO_SERCOS3=y
CONFIG_UIO_PCI_GENERIC=y
CONFIG_UIO_NETX=y
CONFIG_UIO_PRUSS=y
CONFIG_UIO_MF624=y
CONFIG_UIO_DFL=y
CONFIG_VFIO=y
CONFIG_VFIO_VIRQFD=y
CONFIG_VFIO_NOIOMMU=y
CONFIG_VFIO_PCI_CORE=y
CONFIG_VFIO_PCI_MMAP=y
CONFIG_VFIO_PCI_INTX=y
CONFIG_VFIO_PCI=y
CONFIG_MLX5_VFIO_PCI=y
CONFIG_VFIO_PLATFORM=y
CONFIG_VFIO_AMBA=y
CONFIG_VFIO_PLATFORM_CALXEDAXGMAC_RESET=y
CONFIG_VFIO_PLATFORM_AMDXGBE_RESET=y
CONFIG_VFIO_PLATFORM_BCMFLEXRM_RESET=y
CONFIG_VFIO_MDEV=y
CONFIG_IRQ_BYPASS_MANAGER=y
CONFIG_VIRT_DRIVERS=y
CONFIG_VIRTIO_ANCHOR=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI_LIB=y
CONFIG_VIRTIO_PCI_LIB_LEGACY=y
CONFIG_VIRTIO_MENU=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_VDPA=y
CONFIG_VIRTIO_PMEM=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
CONFIG_VIRTIO_DMA_SHARED_BUFFER=y
CONFIG_VDPA=y
CONFIG_VDPA_SIM=y
CONFIG_VDPA_SIM_NET=y
CONFIG_VDPA_SIM_BLOCK=y
CONFIG_VDPA_USER=y
CONFIG_IFCVF=y
CONFIG_MLX5_VDPA=y
CONFIG_MLX5_VDPA_NET=y
CONFIG_VP_VDPA=y
CONFIG_VHOST_IOTLB=y
CONFIG_VHOST_RING=y
CONFIG_VHOST=y
CONFIG_VHOST_MENU=y
CONFIG_VHOST_NET=y
CONFIG_VHOST_SCSI=y
CONFIG_VHOST_VSOCK=y
CONFIG_VHOST_VDPA=y
CONFIG_VHOST_CROSS_ENDIAN_LEGACY=y

#
# Microsoft Hyper-V guest support
#
# end of Microsoft Hyper-V guest support

CONFIG_GREYBUS=y
CONFIG_GREYBUS_ES2=y
CONFIG_COMEDI=y
CONFIG_COMEDI_DEBUG=y
CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB=2048
CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB=20480
CONFIG_COMEDI_MISC_DRIVERS=y
CONFIG_COMEDI_BOND=y
CONFIG_COMEDI_TEST=y
CONFIG_COMEDI_PARPORT=y
CONFIG_COMEDI_SSV_DNP=y
CONFIG_COMEDI_ISA_DRIVERS=y
CONFIG_COMEDI_PCL711=y
CONFIG_COMEDI_PCL724=y
CONFIG_COMEDI_PCL726=y
CONFIG_COMEDI_PCL730=y
CONFIG_COMEDI_PCL812=y
CONFIG_COMEDI_PCL816=y
CONFIG_COMEDI_PCL818=y
CONFIG_COMEDI_PCM3724=y
CONFIG_COMEDI_AMPLC_DIO200_ISA=y
CONFIG_COMEDI_AMPLC_PC236_ISA=y
CONFIG_COMEDI_AMPLC_PC263_ISA=y
CONFIG_COMEDI_RTI800=y
CONFIG_COMEDI_RTI802=y
CONFIG_COMEDI_DAC02=y
CONFIG_COMEDI_DAS16M1=y
CONFIG_COMEDI_DAS08_ISA=y
CONFIG_COMEDI_DAS16=y
CONFIG_COMEDI_DAS800=y
CONFIG_COMEDI_DAS1800=y
CONFIG_COMEDI_DAS6402=y
CONFIG_COMEDI_DT2801=y
CONFIG_COMEDI_DT2811=y
CONFIG_COMEDI_DT2814=y
CONFIG_COMEDI_DT2815=y
CONFIG_COMEDI_DT2817=y
CONFIG_COMEDI_DT282X=y
CONFIG_COMEDI_DMM32AT=y
CONFIG_COMEDI_FL512=y
CONFIG_COMEDI_AIO_AIO12_8=y
CONFIG_COMEDI_AIO_IIRO_16=y
CONFIG_COMEDI_II_PCI20KC=y
CONFIG_COMEDI_C6XDIGIO=y
CONFIG_COMEDI_MPC624=y
CONFIG_COMEDI_ADQ12B=y
CONFIG_COMEDI_NI_AT_A2150=y
CONFIG_COMEDI_NI_AT_AO=y
CONFIG_COMEDI_NI_ATMIO=y
CONFIG_COMEDI_NI_ATMIO16D=y
CONFIG_COMEDI_NI_LABPC_ISA=y
CONFIG_COMEDI_PCMAD=y
CONFIG_COMEDI_PCMDA12=y
CONFIG_COMEDI_PCMMIO=y
CONFIG_COMEDI_PCMUIO=y
CONFIG_COMEDI_MULTIQ3=y
CONFIG_COMEDI_S526=y
CONFIG_COMEDI_PCI_DRIVERS=y
CONFIG_COMEDI_8255_PCI=y
CONFIG_COMEDI_ADDI_WATCHDOG=y
CONFIG_COMEDI_ADDI_APCI_1032=y
CONFIG_COMEDI_ADDI_APCI_1500=y
CONFIG_COMEDI_ADDI_APCI_1516=y
CONFIG_COMEDI_ADDI_APCI_1564=y
CONFIG_COMEDI_ADDI_APCI_16XX=y
CONFIG_COMEDI_ADDI_APCI_2032=y
CONFIG_COMEDI_ADDI_APCI_2200=y
CONFIG_COMEDI_ADDI_APCI_3120=y
CONFIG_COMEDI_ADDI_APCI_3501=y
CONFIG_COMEDI_ADDI_APCI_3XXX=y
CONFIG_COMEDI_ADL_PCI6208=y
CONFIG_COMEDI_ADL_PCI7X3X=y
CONFIG_COMEDI_ADL_PCI8164=y
CONFIG_COMEDI_ADL_PCI9111=y
CONFIG_COMEDI_ADL_PCI9118=y
CONFIG_COMEDI_ADV_PCI1710=y
CONFIG_COMEDI_ADV_PCI1720=y
CONFIG_COMEDI_ADV_PCI1723=y
CONFIG_COMEDI_ADV_PCI1724=y
CONFIG_COMEDI_ADV_PCI1760=y
CONFIG_COMEDI_ADV_PCI_DIO=y
CONFIG_COMEDI_AMPLC_DIO200_PCI=y
CONFIG_COMEDI_AMPLC_PC236_PCI=y
CONFIG_COMEDI_AMPLC_PC263_PCI=y
CONFIG_COMEDI_AMPLC_PCI224=y
CONFIG_COMEDI_AMPLC_PCI230=y
CONFIG_COMEDI_CONTEC_PCI_DIO=y
CONFIG_COMEDI_DAS08_PCI=y
CONFIG_COMEDI_DT3000=y
CONFIG_COMEDI_DYNA_PCI10XX=y
CONFIG_COMEDI_GSC_HPDI=y
CONFIG_COMEDI_MF6X4=y
CONFIG_COMEDI_ICP_MULTI=y
CONFIG_COMEDI_DAQBOARD2000=y
CONFIG_COMEDI_JR3_PCI=y
CONFIG_COMEDI_KE_COUNTER=y
CONFIG_COMEDI_CB_PCIDAS64=y
CONFIG_COMEDI_CB_PCIDAS=y
CONFIG_COMEDI_CB_PCIDDA=y
CONFIG_COMEDI_CB_PCIMDAS=y
CONFIG_COMEDI_CB_PCIMDDA=y
CONFIG_COMEDI_ME4000=y
CONFIG_COMEDI_ME_DAQ=y
CONFIG_COMEDI_NI_6527=y
CONFIG_COMEDI_NI_65XX=y
CONFIG_COMEDI_NI_660X=y
CONFIG_COMEDI_NI_670X=y
CONFIG_COMEDI_NI_LABPC_PCI=y
CONFIG_COMEDI_NI_PCIDIO=y
CONFIG_COMEDI_NI_PCIMIO=y
CONFIG_COMEDI_RTD520=y
CONFIG_COMEDI_S626=y
CONFIG_COMEDI_MITE=y
CONFIG_COMEDI_NI_TIOCMD=y
CONFIG_COMEDI_PCMCIA_DRIVERS=y
CONFIG_COMEDI_CB_DAS16_CS=y
CONFIG_COMEDI_DAS08_CS=y
CONFIG_COMEDI_NI_DAQ_700_CS=y
CONFIG_COMEDI_NI_DAQ_DIO24_CS=y
CONFIG_COMEDI_NI_LABPC_CS=y
CONFIG_COMEDI_NI_MIO_CS=y
CONFIG_COMEDI_QUATECH_DAQP_CS=y
CONFIG_COMEDI_USB_DRIVERS=y
CONFIG_COMEDI_DT9812=y
CONFIG_COMEDI_NI_USB6501=y
CONFIG_COMEDI_USBDUX=y
CONFIG_COMEDI_USBDUXFAST=y
CONFIG_COMEDI_USBDUXSIGMA=y
CONFIG_COMEDI_VMK80XX=y
CONFIG_COMEDI_8254=y
CONFIG_COMEDI_8255=y
CONFIG_COMEDI_8255_SA=y
CONFIG_COMEDI_KCOMEDILIB=y
CONFIG_COMEDI_AMPLC_DIO200=y
CONFIG_COMEDI_AMPLC_PC236=y
CONFIG_COMEDI_DAS08=y
CONFIG_COMEDI_NI_LABPC=y
CONFIG_COMEDI_NI_TIO=y
CONFIG_COMEDI_NI_ROUTING=y
CONFIG_COMEDI_TESTS=y
CONFIG_COMEDI_TESTS_EXAMPLE=y
CONFIG_COMEDI_TESTS_NI_ROUTES=y
CONFIG_STAGING=y
CONFIG_PRISM2_USB=y
CONFIG_RTL8192U=m
CONFIG_RTLLIB=m
CONFIG_RTLLIB_CRYPTO_CCMP=m
CONFIG_RTLLIB_CRYPTO_TKIP=m
CONFIG_RTLLIB_CRYPTO_WEP=m
CONFIG_RTL8192E=m
CONFIG_RTL8723BS=m
CONFIG_R8712U=y
CONFIG_R8188EU=m
CONFIG_RTS5208=y
CONFIG_OCTEON_ETHERNET=y
CONFIG_VT6655=m
CONFIG_VT6656=m

#
# IIO staging drivers
#

#
# Accelerometers
#
CONFIG_ADIS16203=y
CONFIG_ADIS16240=y
# end of Accelerometers

#
# Analog to digital converters
#
CONFIG_AD7816=y
# end of Analog to digital converters

#
# Analog digital bi-direction converters
#
CONFIG_ADT7316=y
CONFIG_ADT7316_SPI=y
CONFIG_ADT7316_I2C=y
# end of Analog digital bi-direction converters

#
# Direct Digital Synthesis
#
CONFIG_AD9832=y
CONFIG_AD9834=y
# end of Direct Digital Synthesis

#
# Network Analyzer, Impedance Converters
#
CONFIG_AD5933=y
# end of Network Analyzer, Impedance Converters

#
# Active energy metering IC
#
CONFIG_ADE7854=y
CONFIG_ADE7854_I2C=y
CONFIG_ADE7854_SPI=y
# end of Active energy metering IC

#
# Resolver to digital converters
#
CONFIG_AD2S1210=y
# end of Resolver to digital converters
# end of IIO staging drivers

CONFIG_FB_SM750=y
CONFIG_USB_EMXX=y
CONFIG_STAGING_MEDIA=y
CONFIG_VIDEO_IMX_MEDIA=y

#
# i.MX5/6/7/8 Media Sub devices
#
CONFIG_VIDEO_IMX_CSI=y
CONFIG_VIDEO_IMX7_CSI=y
# end of i.MX5/6/7/8 Media Sub devices

CONFIG_VIDEO_MAX96712=y
CONFIG_VIDEO_MESON_VDEC=y
CONFIG_VIDEO_OMAP4=y
CONFIG_VIDEO_ROCKCHIP_VDEC=y
CONFIG_VIDEO_SUNXI=y
CONFIG_VIDEO_SUNXI_CEDRUS=y
CONFIG_STAGING_MEDIA_DEPRECATED=y
CONFIG_VIDEO_CPIA2=y
CONFIG_VIDEO_VIU=y
CONFIG_VIDEO_SAA7146=y
CONFIG_VIDEO_SAA7146_VV=y
CONFIG_DVB_AV7110_IR=y
CONFIG_DVB_AV7110=y
CONFIG_DVB_AV7110_OSD=y
CONFIG_DVB_BUDGET_PATCH=y
CONFIG_DVB_SP8870=y
CONFIG_VIDEO_HEXIUM_GEMINI=y
CONFIG_VIDEO_HEXIUM_ORION=y
CONFIG_VIDEO_MXB=y
CONFIG_DVB_BUDGET_CORE=y
CONFIG_DVB_BUDGET=y
CONFIG_DVB_BUDGET_CI=y
CONFIG_DVB_BUDGET_AV=y
CONFIG_VIDEO_STKWEBCAM=y
CONFIG_VIDEO_TM6000=y
CONFIG_VIDEO_TM6000_ALSA=y
CONFIG_VIDEO_TM6000_DVB=y
CONFIG_VIDEO_DM6446_CCDC=y
CONFIG_VIDEO_DM355_CCDC=y
CONFIG_VIDEO_DM365_ISIF=y
CONFIG_USB_ZR364XX=y
CONFIG_LTE_GDM724X=m
CONFIG_FB_TFT=y
CONFIG_FB_TFT_AGM1264K_FL=y
CONFIG_FB_TFT_BD663474=y
CONFIG_FB_TFT_HX8340BN=y
CONFIG_FB_TFT_HX8347D=y
CONFIG_FB_TFT_HX8353D=y
CONFIG_FB_TFT_HX8357D=y
CONFIG_FB_TFT_ILI9163=y
CONFIG_FB_TFT_ILI9320=y
CONFIG_FB_TFT_ILI9325=y
CONFIG_FB_TFT_ILI9340=y
CONFIG_FB_TFT_ILI9341=y
CONFIG_FB_TFT_ILI9481=y
CONFIG_FB_TFT_ILI9486=y
CONFIG_FB_TFT_PCD8544=y
CONFIG_FB_TFT_RA8875=y
CONFIG_FB_TFT_S6D02A1=y
CONFIG_FB_TFT_S6D1121=y
CONFIG_FB_TFT_SEPS525=y
CONFIG_FB_TFT_SH1106=y
CONFIG_FB_TFT_SSD1289=y
CONFIG_FB_TFT_SSD1305=y
CONFIG_FB_TFT_SSD1306=y
CONFIG_FB_TFT_SSD1331=y
CONFIG_FB_TFT_SSD1351=y
CONFIG_FB_TFT_ST7735R=y
CONFIG_FB_TFT_ST7789V=y
CONFIG_FB_TFT_TINYLCD=y
CONFIG_FB_TFT_TLS8204=y
CONFIG_FB_TFT_UC1611=y
CONFIG_FB_TFT_UC1701=y
CONFIG_FB_TFT_UPD161704=y
CONFIG_MOST_COMPONENTS=y
CONFIG_MOST_NET=y
CONFIG_MOST_VIDEO=y
CONFIG_MOST_DIM2=y
CONFIG_MOST_I2C=y
CONFIG_KS7010=y
CONFIG_GREYBUS_AUDIO=y
CONFIG_GREYBUS_AUDIO_APB_CODEC=y
CONFIG_GREYBUS_BOOTROM=y
CONFIG_GREYBUS_FIRMWARE=y
CONFIG_GREYBUS_HID=y
CONFIG_GREYBUS_LIGHT=y
CONFIG_GREYBUS_LOG=y
CONFIG_GREYBUS_LOOPBACK=y
CONFIG_GREYBUS_POWER=y
CONFIG_GREYBUS_RAW=y
CONFIG_GREYBUS_VIBRATOR=y
CONFIG_GREYBUS_BRIDGED_PHY=y
CONFIG_GREYBUS_GPIO=y
CONFIG_GREYBUS_I2C=y
CONFIG_GREYBUS_PWM=y
CONFIG_GREYBUS_SDIO=y
CONFIG_GREYBUS_SPI=y
CONFIG_GREYBUS_UART=y
CONFIG_GREYBUS_USB=y
CONFIG_GREYBUS_ARCHE=y
CONFIG_BCM_VIDEOCORE=y
CONFIG_BCM2835_VCHIQ=y
CONFIG_VCHIQ_CDEV=y
CONFIG_SND_BCM2835=y
CONFIG_VIDEO_BCM2835=y
CONFIG_BCM2835_VCHIQ_MMAL=y
CONFIG_PI433=y
CONFIG_XIL_AXIS_FIFO=y
CONFIG_FIELDBUS_DEV=y
CONFIG_HMS_ANYBUSS_BUS=y
CONFIG_ARCX_ANYBUS_CONTROLLER=y
CONFIG_HMS_PROFINET=y
CONFIG_QLGE=y
CONFIG_VME_BUS=y

#
# VME Bridge Drivers
#
CONFIG_VME_TSI148=y
CONFIG_VME_FAKE=y

#
# VME Device Drivers
#
CONFIG_VME_USER=y
CONFIG_GOLDFISH=y
CONFIG_GOLDFISH_PIPE=y
CONFIG_CHROME_PLATFORMS=y
CONFIG_CROS_EC=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_RPMSG=y
CONFIG_CROS_EC_SPI=y
CONFIG_CROS_EC_PROTO=y
CONFIG_CROS_KBD_LED_BACKLIGHT=y
CONFIG_CROS_EC_CHARDEV=y
CONFIG_CROS_EC_LIGHTBAR=y
CONFIG_CROS_EC_VBC=y
CONFIG_CROS_EC_DEBUGFS=y
CONFIG_CROS_EC_SENSORHUB=y
CONFIG_CROS_EC_SYSFS=y
CONFIG_CROS_EC_TYPEC=y
CONFIG_CROS_USBPD_LOGGER=y
CONFIG_CROS_USBPD_NOTIFY=y
CONFIG_CROS_KUNIT=y
CONFIG_MELLANOX_PLATFORM=y
CONFIG_MLXREG_HOTPLUG=y
CONFIG_MLXREG_IO=y
CONFIG_MLXREG_LC=y
CONFIG_NVSW_SN2201=y
CONFIG_OLPC_EC=y
CONFIG_OLPC_XO175=y
CONFIG_OLPC_XO175_EC=y
CONFIG_SURFACE_PLATFORMS=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_CLK_PREPARE=y
CONFIG_COMMON_CLK=y
CONFIG_COMMON_CLK_WM831X=y

#
# Clock driver for ARM Reference designs
#
CONFIG_CLK_ICST=y
CONFIG_CLK_SP810=y
# end of Clock driver for ARM Reference designs

CONFIG_CLK_HSDK=y
CONFIG_LMK04832=y
CONFIG_COMMON_CLK_APPLE_NCO=y
CONFIG_COMMON_CLK_MAX77686=y
CONFIG_COMMON_CLK_MAX9485=y
CONFIG_COMMON_CLK_RK808=y
CONFIG_COMMON_CLK_HI655X=y
CONFIG_COMMON_CLK_SCMI=y
CONFIG_COMMON_CLK_SCPI=y
CONFIG_COMMON_CLK_SI5341=y
CONFIG_COMMON_CLK_SI5351=y
CONFIG_COMMON_CLK_SI514=y
CONFIG_COMMON_CLK_SI544=y
CONFIG_COMMON_CLK_SI570=y
CONFIG_COMMON_CLK_BM1880=y
CONFIG_COMMON_CLK_CDCE706=y
CONFIG_COMMON_CLK_TPS68470=y
CONFIG_COMMON_CLK_CDCE925=y
CONFIG_COMMON_CLK_CS2000_CP=y
CONFIG_COMMON_CLK_EN7523=y
CONFIG_COMMON_CLK_FSL_FLEXSPI=y
CONFIG_COMMON_CLK_FSL_SAI=y
CONFIG_COMMON_CLK_GEMINI=y
CONFIG_COMMON_CLK_LAN966X=y
CONFIG_COMMON_CLK_ASPEED=y
CONFIG_COMMON_CLK_S2MPS11=y
CONFIG_CLK_TWL6040=y
CONFIG_COMMON_CLK_AXI_CLKGEN=y
CONFIG_CLK_QORIQ=y
CONFIG_CLK_LS1028A_PLLDIG=y
CONFIG_COMMON_CLK_XGENE=y
CONFIG_COMMON_CLK_LOCHNAGAR=y
CONFIG_COMMON_CLK_PALMAS=y
CONFIG_COMMON_CLK_PWM=y
CONFIG_COMMON_CLK_OXNAS=y
CONFIG_COMMON_CLK_RS9_PCIE=y
CONFIG_COMMON_CLK_VC5=y
CONFIG_COMMON_CLK_VC7=y
CONFIG_COMMON_CLK_MMP2_AUDIO=y
CONFIG_COMMON_CLK_BD718XX=y
CONFIG_COMMON_CLK_FIXED_MMIO=y
CONFIG_CLK_ACTIONS=y
CONFIG_CLK_OWL_S500=y
CONFIG_CLK_OWL_S700=y
CONFIG_CLK_OWL_S900=y
CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC=y
CONFIG_CLK_BAIKAL_T1=y
CONFIG_CLK_BT1_CCU_PLL=y
CONFIG_CLK_BT1_CCU_DIV=y
CONFIG_CLK_BT1_CCU_RST=y
CONFIG_CLK_BCM2711_DVP=y
CONFIG_CLK_BCM2835=y
CONFIG_CLK_BCM_63XX=y
CONFIG_CLK_BCM_63XX_GATE=y
CONFIG_CLK_BCM_KONA=y
CONFIG_COMMON_CLK_IPROC=y
CONFIG_CLK_BCM_CYGNUS=y
CONFIG_CLK_BCM_HR2=y
CONFIG_CLK_BCM_NSP=y
CONFIG_CLK_BCM_NS2=y
CONFIG_CLK_BCM_SR=y
CONFIG_CLK_RASPBERRYPI=y
CONFIG_COMMON_CLK_HI3516CV300=y
CONFIG_COMMON_CLK_HI3519=y
CONFIG_COMMON_CLK_HI3559A=y
CONFIG_COMMON_CLK_HI3660=y
CONFIG_COMMON_CLK_HI3670=y
CONFIG_COMMON_CLK_HI3798CV200=y
CONFIG_COMMON_CLK_HI6220=y
CONFIG_RESET_HISI=y
CONFIG_STUB_CLK_HI6220=y
CONFIG_STUB_CLK_HI3660=y
CONFIG_COMMON_CLK_BOSTON=y
CONFIG_MXC_CLK=y
CONFIG_CLK_IMX8MM=y
CONFIG_CLK_IMX8MN=y
CONFIG_CLK_IMX8MP=y
CONFIG_CLK_IMX8MQ=y
CONFIG_CLK_IMX8ULP=y
CONFIG_CLK_IMX93=y

#
# Ingenic SoCs drivers
#
CONFIG_INGENIC_CGU_COMMON=y
CONFIG_INGENIC_CGU_JZ4740=y
CONFIG_INGENIC_CGU_JZ4725B=y
CONFIG_INGENIC_CGU_JZ4760=y
CONFIG_INGENIC_CGU_JZ4770=y
CONFIG_INGENIC_CGU_JZ4780=y
CONFIG_INGENIC_CGU_X1000=y
CONFIG_INGENIC_CGU_X1830=y
CONFIG_INGENIC_TCU_CLK=y
# end of Ingenic SoCs drivers

CONFIG_COMMON_CLK_KEYSTONE=y
CONFIG_TI_SYSCON_CLK=y

#
# Clock driver for MediaTek SoC
#
CONFIG_COMMON_CLK_MEDIATEK=y
CONFIG_COMMON_CLK_MT2701=y
CONFIG_COMMON_CLK_MT2701_MMSYS=y
CONFIG_COMMON_CLK_MT2701_IMGSYS=y
CONFIG_COMMON_CLK_MT2701_VDECSYS=y
CONFIG_COMMON_CLK_MT2701_HIFSYS=y
CONFIG_COMMON_CLK_MT2701_ETHSYS=y
CONFIG_COMMON_CLK_MT2701_BDPSYS=y
CONFIG_COMMON_CLK_MT2701_AUDSYS=y
CONFIG_COMMON_CLK_MT2701_G3DSYS=y
CONFIG_COMMON_CLK_MT2712=y
CONFIG_COMMON_CLK_MT2712_BDPSYS=y
CONFIG_COMMON_CLK_MT2712_IMGSYS=y
CONFIG_COMMON_CLK_MT2712_JPGDECSYS=y
CONFIG_COMMON_CLK_MT2712_MFGCFG=y
CONFIG_COMMON_CLK_MT2712_MMSYS=y
CONFIG_COMMON_CLK_MT2712_VDECSYS=y
CONFIG_COMMON_CLK_MT2712_VENCSYS=y
CONFIG_COMMON_CLK_MT6765=y
CONFIG_COMMON_CLK_MT6765_AUDIOSYS=y
CONFIG_COMMON_CLK_MT6765_CAMSYS=y
CONFIG_COMMON_CLK_MT6765_GCESYS=y
CONFIG_COMMON_CLK_MT6765_MMSYS=y
CONFIG_COMMON_CLK_MT6765_IMGSYS=y
CONFIG_COMMON_CLK_MT6765_VCODECSYS=y
CONFIG_COMMON_CLK_MT6765_MFGSYS=y
CONFIG_COMMON_CLK_MT6765_MIPI0ASYS=y
CONFIG_COMMON_CLK_MT6765_MIPI0BSYS=y
CONFIG_COMMON_CLK_MT6765_MIPI1ASYS=y
CONFIG_COMMON_CLK_MT6765_MIPI1BSYS=y
CONFIG_COMMON_CLK_MT6765_MIPI2ASYS=y
CONFIG_COMMON_CLK_MT6765_MIPI2BSYS=y
CONFIG_COMMON_CLK_MT6779=y
CONFIG_COMMON_CLK_MT6779_MMSYS=y
CONFIG_COMMON_CLK_MT6779_IMGSYS=y
CONFIG_COMMON_CLK_MT6779_IPESYS=y
CONFIG_COMMON_CLK_MT6779_CAMSYS=y
CONFIG_COMMON_CLK_MT6779_VDECSYS=y
CONFIG_COMMON_CLK_MT6779_VENCSYS=y
CONFIG_COMMON_CLK_MT6779_MFGCFG=y
CONFIG_COMMON_CLK_MT6779_AUDSYS=y
CONFIG_COMMON_CLK_MT6795=y
CONFIG_COMMON_CLK_MT6795_MFGCFG=y
CONFIG_COMMON_CLK_MT6795_MMSYS=y
CONFIG_COMMON_CLK_MT6795_VDECSYS=y
CONFIG_COMMON_CLK_MT6795_VENCSYS=y
CONFIG_COMMON_CLK_MT6797=y
CONFIG_COMMON_CLK_MT6797_MMSYS=y
CONFIG_COMMON_CLK_MT6797_IMGSYS=y
CONFIG_COMMON_CLK_MT6797_VDECSYS=y
CONFIG_COMMON_CLK_MT6797_VENCSYS=y
CONFIG_COMMON_CLK_MT7622=y
CONFIG_COMMON_CLK_MT7622_ETHSYS=y
CONFIG_COMMON_CLK_MT7622_HIFSYS=y
CONFIG_COMMON_CLK_MT7622_AUDSYS=y
CONFIG_COMMON_CLK_MT7629=y
CONFIG_COMMON_CLK_MT7629_ETHSYS=y
CONFIG_COMMON_CLK_MT7629_HIFSYS=y
CONFIG_COMMON_CLK_MT7986=y
CONFIG_COMMON_CLK_MT7986_ETHSYS=y
CONFIG_COMMON_CLK_MT8135=y
CONFIG_COMMON_CLK_MT8167=y
CONFIG_COMMON_CLK_MT8167_AUDSYS=y
CONFIG_COMMON_CLK_MT8167_IMGSYS=y
CONFIG_COMMON_CLK_MT8167_MFGCFG=y
CONFIG_COMMON_CLK_MT8167_MMSYS=y
CONFIG_COMMON_CLK_MT8167_VDECSYS=y
CONFIG_COMMON_CLK_MT8173=y
CONFIG_COMMON_CLK_MT8173_MMSYS=y
CONFIG_COMMON_CLK_MT8183=y
CONFIG_COMMON_CLK_MT8183_AUDIOSYS=y
CONFIG_COMMON_CLK_MT8183_CAMSYS=y
CONFIG_COMMON_CLK_MT8183_IMGSYS=y
CONFIG_COMMON_CLK_MT8183_IPU_CORE0=y
CONFIG_COMMON_CLK_MT8183_IPU_CORE1=y
CONFIG_COMMON_CLK_MT8183_IPU_ADL=y
CONFIG_COMMON_CLK_MT8183_IPU_CONN=y
CONFIG_COMMON_CLK_MT8183_MFGCFG=y
CONFIG_COMMON_CLK_MT8183_MMSYS=y
CONFIG_COMMON_CLK_MT8183_VDECSYS=y
CONFIG_COMMON_CLK_MT8183_VENCSYS=y
CONFIG_COMMON_CLK_MT8186=y
CONFIG_COMMON_CLK_MT8192=y
CONFIG_COMMON_CLK_MT8192_AUDSYS=y
CONFIG_COMMON_CLK_MT8192_CAMSYS=y
CONFIG_COMMON_CLK_MT8192_IMGSYS=y
CONFIG_COMMON_CLK_MT8192_IMP_IIC_WRAP=y
CONFIG_COMMON_CLK_MT8192_IPESYS=y
CONFIG_COMMON_CLK_MT8192_MDPSYS=y
CONFIG_COMMON_CLK_MT8192_MFGCFG=y
CONFIG_COMMON_CLK_MT8192_MMSYS=y
CONFIG_COMMON_CLK_MT8192_MSDC=y
CONFIG_COMMON_CLK_MT8192_SCP_ADSP=y
CONFIG_COMMON_CLK_MT8192_VDECSYS=y
CONFIG_COMMON_CLK_MT8192_VENCSYS=y
CONFIG_COMMON_CLK_MT8195=y
CONFIG_COMMON_CLK_MT8365=y
CONFIG_COMMON_CLK_MT8365_APU=y
CONFIG_COMMON_CLK_MT8365_CAM=y
CONFIG_COMMON_CLK_MT8365_MFG=y
CONFIG_COMMON_CLK_MT8365_MMSYS=y
CONFIG_COMMON_CLK_MT8365_VDEC=y
CONFIG_COMMON_CLK_MT8365_VENC=y
CONFIG_COMMON_CLK_MT8516=y
CONFIG_COMMON_CLK_MT8516_AUDSYS=y
# end of Clock driver for MediaTek SoC

#
# Clock support for Amlogic platforms
#
# CONFIG_COMMON_CLK_AXG_AUDIO is not set
# end of Clock support for Amlogic platforms

CONFIG_MSTAR_MSC313_MPLL=y
CONFIG_MCHP_CLK_MPFS=y
CONFIG_COMMON_CLK_PISTACHIO=y
CONFIG_QCOM_GDSC=y
CONFIG_QCOM_RPMCC=y
CONFIG_COMMON_CLK_QCOM=y
CONFIG_QCOM_A53PLL=y
CONFIG_QCOM_A7PLL=y
CONFIG_QCOM_CLK_APCS_MSM8916=y
CONFIG_QCOM_CLK_APCS_SDX55=y
CONFIG_QCOM_CLK_SMD_RPM=y
CONFIG_QCOM_CLK_RPMH=y
CONFIG_APQ_GCC_8084=y
CONFIG_APQ_MMCC_8084=y
CONFIG_IPQ_APSS_PLL=y
CONFIG_IPQ_APSS_6018=y
CONFIG_IPQ_GCC_4019=y
CONFIG_IPQ_GCC_6018=y
CONFIG_IPQ_GCC_806X=y
CONFIG_IPQ_LCC_806X=y
CONFIG_IPQ_GCC_8074=y
CONFIG_MSM_GCC_8660=y
CONFIG_MSM_GCC_8909=y
CONFIG_MSM_GCC_8916=y
CONFIG_MSM_GCC_8939=y
CONFIG_MSM_GCC_8960=y
CONFIG_MSM_LCC_8960=y
CONFIG_MDM_GCC_9607=y
CONFIG_MDM_GCC_9615=y
CONFIG_MDM_LCC_9615=y
CONFIG_MSM_MMCC_8960=y
CONFIG_MSM_GCC_8953=y
CONFIG_MSM_GCC_8974=y
CONFIG_MSM_MMCC_8974=y
CONFIG_MSM_GCC_8976=y
CONFIG_MSM_MMCC_8994=y
CONFIG_MSM_GCC_8994=y
CONFIG_MSM_GCC_8996=y
CONFIG_MSM_MMCC_8996=y
CONFIG_MSM_GCC_8998=y
CONFIG_MSM_GPUCC_8998=y
CONFIG_MSM_MMCC_8998=y
CONFIG_QCM_GCC_2290=y
CONFIG_QCM_DISPCC_2290=y
CONFIG_QCS_GCC_404=y
CONFIG_SC_CAMCC_7180=y
CONFIG_SC_CAMCC_7280=y
CONFIG_SC_DISPCC_7180=y
CONFIG_SC_DISPCC_7280=y
CONFIG_SC_GCC_7180=y
CONFIG_SC_GCC_7280=y
CONFIG_SC_GCC_8180X=y
CONFIG_SC_GCC_8280XP=y
CONFIG_SC_GPUCC_7180=y
CONFIG_SC_GPUCC_7280=y
CONFIG_SC_GPUCC_8280XP=y
CONFIG_SC_LPASSCC_7280=y
CONFIG_SC_LPASS_CORECC_7180=y
CONFIG_SC_LPASS_CORECC_7280=y
CONFIG_SC_MSS_7180=y
CONFIG_SC_VIDEOCC_7180=y
CONFIG_SC_VIDEOCC_7280=y
CONFIG_SDM_CAMCC_845=y
CONFIG_SDM_GCC_660=y
CONFIG_SDM_MMCC_660=y
CONFIG_SDM_GPUCC_660=y
CONFIG_QCS_TURING_404=y
CONFIG_QCS_Q6SSTOP_404=y
CONFIG_SDM_GCC_845=y
CONFIG_SDM_GPUCC_845=y
CONFIG_SDM_VIDEOCC_845=y
CONFIG_SDM_DISPCC_845=y
CONFIG_SDM_LPASSCC_845=y
CONFIG_SDX_GCC_55=y
CONFIG_SDX_GCC_65=y
CONFIG_SM_CAMCC_8250=y
CONFIG_SM_CAMCC_8450=y
CONFIG_SM_DISPCC_6115=y
CONFIG_SM_DISPCC_6125=y
CONFIG_SM_DISPCC_8250=y
CONFIG_SM_DISPCC_6350=y
CONFIG_SM_DISPCC_8450=y
CONFIG_SM_GCC_6115=y
CONFIG_SM_GCC_6125=y
CONFIG_SM_GCC_6350=y
CONFIG_SM_GCC_6375=y
CONFIG_SM_GCC_8150=y
CONFIG_SM_GCC_8250=y
CONFIG_SM_GCC_8350=y
CONFIG_SM_GCC_8450=y
CONFIG_SM_GPUCC_6350=y
CONFIG_SM_GPUCC_8150=y
CONFIG_SM_GPUCC_8250=y
CONFIG_SM_GPUCC_8350=y
CONFIG_SM_VIDEOCC_8150=y
CONFIG_SM_VIDEOCC_8250=y
CONFIG_SPMI_PMIC_CLKDIV=y
CONFIG_QCOM_HFPLL=y
CONFIG_KPSS_XCC=y
CONFIG_CLK_GFM_LPASS_SM8250=y
CONFIG_CLK_MT7621=y
CONFIG_CLK_RENESAS=y
CONFIG_CLK_EMEV2=y
CONFIG_CLK_RZA1=y
CONFIG_CLK_R7S9210=y
CONFIG_CLK_R8A73A4=y
CONFIG_CLK_R8A7740=y
CONFIG_CLK_R8A7742=y
CONFIG_CLK_R8A7743=y
CONFIG_CLK_R8A7745=y
CONFIG_CLK_R8A77470=y
CONFIG_CLK_R8A774A1=y
CONFIG_CLK_R8A774B1=y
CONFIG_CLK_R8A774C0=y
CONFIG_CLK_R8A774E1=y
CONFIG_CLK_R8A7778=y
CONFIG_CLK_R8A7779=y
CONFIG_CLK_R8A7790=y
CONFIG_CLK_R8A7791=y
CONFIG_CLK_R8A7792=y
CONFIG_CLK_R8A7794=y
CONFIG_CLK_R8A7795=y
CONFIG_CLK_R8A77960=y
CONFIG_CLK_R8A77961=y
CONFIG_CLK_R8A77965=y
CONFIG_CLK_R8A77970=y
CONFIG_CLK_R8A77980=y
CONFIG_CLK_R8A77990=y
CONFIG_CLK_R8A77995=y
CONFIG_CLK_R8A779A0=y
CONFIG_CLK_R8A779F0=y
CONFIG_CLK_R8A779G0=y
CONFIG_CLK_R9A06G032=y
CONFIG_CLK_R9A07G043=y
CONFIG_CLK_R9A07G044=y
CONFIG_CLK_R9A07G054=y
CONFIG_CLK_R9A09G011=y
CONFIG_CLK_SH73A0=y
CONFIG_CLK_RCAR_CPG_LIB=y
CONFIG_CLK_RCAR_GEN2_CPG=y
CONFIG_CLK_RCAR_GEN3_CPG=y
CONFIG_CLK_RCAR_GEN4_CPG=y
CONFIG_CLK_RCAR_USB2_CLOCK_SEL=y
CONFIG_CLK_RZG2L=y
CONFIG_CLK_RENESAS_CPG_MSSR=y
CONFIG_CLK_RENESAS_CPG_MSTP=y
CONFIG_CLK_RENESAS_DIV6=y
CONFIG_COMMON_CLK_SAMSUNG=y
CONFIG_S3C64XX_COMMON_CLK=y
CONFIG_S5PV210_COMMON_CLK=y
CONFIG_EXYNOS_3250_COMMON_CLK=y
CONFIG_EXYNOS_4_COMMON_CLK=y
CONFIG_EXYNOS_5250_COMMON_CLK=y
CONFIG_EXYNOS_5260_COMMON_CLK=y
CONFIG_EXYNOS_5410_COMMON_CLK=y
CONFIG_EXYNOS_5420_COMMON_CLK=y
CONFIG_EXYNOS_ARM64_COMMON_CLK=y
CONFIG_EXYNOS_AUDSS_CLK_CON=y
CONFIG_EXYNOS_CLKOUT=y
CONFIG_S3C2410_COMMON_CLK=y
CONFIG_S3C2412_COMMON_CLK=y
CONFIG_S3C2443_COMMON_CLK=y
CONFIG_TESLA_FSD_COMMON_CLK=y
CONFIG_CLK_SIFIVE=y
CONFIG_CLK_SIFIVE_PRCI=y
CONFIG_CLK_INTEL_SOCFPGA=y
CONFIG_CLK_INTEL_SOCFPGA32=y
CONFIG_CLK_INTEL_SOCFPGA64=y
CONFIG_SPRD_COMMON_CLK=y
CONFIG_SPRD_SC9860_CLK=y
CONFIG_SPRD_SC9863A_CLK=y
CONFIG_SPRD_UMS512_CLK=y
CONFIG_CLK_STARFIVE_JH7100=y
CONFIG_CLK_STARFIVE_JH7100_AUDIO=y
CONFIG_CLK_SUNXI=y
CONFIG_CLK_SUNXI_CLOCKS=y
CONFIG_CLK_SUNXI_PRCM_SUN6I=y
CONFIG_CLK_SUNXI_PRCM_SUN8I=y
CONFIG_CLK_SUNXI_PRCM_SUN9I=y
CONFIG_SUNXI_CCU=y
CONFIG_SUNIV_F1C100S_CCU=y
CONFIG_SUN20I_D1_CCU=y
CONFIG_SUN20I_D1_R_CCU=y
CONFIG_SUN50I_A64_CCU=y
CONFIG_SUN50I_A100_CCU=y
CONFIG_SUN50I_A100_R_CCU=y
CONFIG_SUN50I_H6_CCU=y
CONFIG_SUN50I_H616_CCU=y
CONFIG_SUN50I_H6_R_CCU=y
CONFIG_SUN4I_A10_CCU=y
CONFIG_SUN5I_CCU=y
CONFIG_SUN6I_A31_CCU=y
CONFIG_SUN6I_RTC_CCU=y
CONFIG_SUN8I_A23_CCU=y
CONFIG_SUN8I_A33_CCU=y
CONFIG_SUN8I_A83T_CCU=y
CONFIG_SUN8I_H3_CCU=y
CONFIG_SUN8I_V3S_CCU=y
CONFIG_SUN8I_DE2_CCU=y
CONFIG_SUN8I_R40_CCU=y
CONFIG_SUN9I_A80_CCU=y
CONFIG_SUN8I_R_CCU=y
CONFIG_COMMON_CLK_TI_ADPLL=y
CONFIG_CLK_UNIPHIER=y
CONFIG_COMMON_CLK_VISCONTI=y
CONFIG_CLK_LGM_CGU=y
CONFIG_XILINX_VCU=y
CONFIG_COMMON_CLK_XLNX_CLKWZRD=y
CONFIG_COMMON_CLK_ZYNQMP=y
CONFIG_CLK_KUNIT_TEST=y
CONFIG_CLK_GATE_KUNIT_TEST=y
CONFIG_HWSPINLOCK=y
CONFIG_HWSPINLOCK_OMAP=y
CONFIG_HWSPINLOCK_QCOM=y
CONFIG_HWSPINLOCK_SPRD=y
CONFIG_HWSPINLOCK_STM32=y
CONFIG_HWSPINLOCK_SUN6I=y
CONFIG_HSEM_U8500=y

#
# Clock Source drivers
#
CONFIG_TIMER_OF=y
CONFIG_TIMER_PROBE=y
CONFIG_CLKSRC_MMIO=y
CONFIG_BCM2835_TIMER=y
CONFIG_BCM_KONA_TIMER=y
CONFIG_DAVINCI_TIMER=y
CONFIG_DIGICOLOR_TIMER=y
CONFIG_OMAP_DM_TIMER=y
CONFIG_DW_APB_TIMER=y
CONFIG_FTTMR010_TIMER=y
CONFIG_IXP4XX_TIMER=y
CONFIG_MESON6_TIMER=y
CONFIG_OWL_TIMER=y
CONFIG_RDA_TIMER=y
CONFIG_SUN4I_TIMER=y
CONFIG_SUN5I_HSTIMER=y
CONFIG_TEGRA_TIMER=y
CONFIG_TEGRA186_TIMER=y
CONFIG_VT8500_TIMER=y
CONFIG_NPCM7XX_TIMER=y
CONFIG_CADENCE_TTC_TIMER=y
CONFIG_ASM9260_TIMER=y
CONFIG_CLKSRC_DBX500_PRCMU=y
CONFIG_CLPS711X_TIMER=y
CONFIG_MXS_TIMER=y
CONFIG_NSPIRE_TIMER=y
CONFIG_INTEGRATOR_AP_TIMER=y
CONFIG_CLKSRC_PISTACHIO=y
CONFIG_CLKSRC_STM32_LP=y
CONFIG_ARMV7M_SYSTICK=y
CONFIG_ATMEL_PIT=y
CONFIG_ATMEL_ST=y
CONFIG_CLKSRC_SAMSUNG_PWM=y
CONFIG_FSL_FTM_TIMER=y
CONFIG_OXNAS_RPS_TIMER=y
CONFIG_MTK_TIMER=y
CONFIG_SPRD_TIMER=y
CONFIG_CLKSRC_JCORE_PIT=y
CONFIG_SH_TIMER_CMT=y
CONFIG_SH_TIMER_MTU2=y
CONFIG_RENESAS_OSTM=y
CONFIG_SH_TIMER_TMU=y
CONFIG_EM_TIMER_STI=y
CONFIG_CLKSRC_PXA=y
CONFIG_TIMER_IMX_SYS_CTR=y
CONFIG_CLKSRC_ST_LPC=y
CONFIG_GXP_TIMER=y
CONFIG_MSC313E_TIMER=y
CONFIG_INGENIC_TIMER=y
CONFIG_INGENIC_SYSOST=y
CONFIG_INGENIC_OST=y
CONFIG_MICROCHIP_PIT64B=y
CONFIG_GOLDFISH_TIMER=y
# end of Clock Source drivers

CONFIG_MAILBOX=y
CONFIG_IMX_MBOX=y
CONFIG_PLATFORM_MHU=y
CONFIG_ARMADA_37XX_RWTM_MBOX=y
CONFIG_ROCKCHIP_MBOX=y
CONFIG_ALTERA_MBOX=y
CONFIG_HI3660_MBOX=y
CONFIG_HI6220_MBOX=y
CONFIG_MAILBOX_TEST=y
CONFIG_POLARFIRE_SOC_MAILBOX=y
CONFIG_QCOM_APCS_IPC=y
CONFIG_BCM_PDC_MBOX=y
CONFIG_STM32_IPCC=y
CONFIG_MTK_ADSP_MBOX=y
CONFIG_MTK_CMDQ_MBOX=y
CONFIG_SUN6I_MSGBOX=y
CONFIG_SPRD_MBOX=y
CONFIG_QCOM_IPCC=y
CONFIG_IOMMU_IOVA=y
CONFIG_IOMMU_API=y
CONFIG_IOMMU_SUPPORT=y

#
# Generic IOMMU Pagetable Support
#
CONFIG_IOMMU_IO_PGTABLE=y
CONFIG_IOMMU_IO_PGTABLE_LPAE=y
CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST=y
CONFIG_IOMMU_IO_PGTABLE_ARMV7S=y
CONFIG_IOMMU_IO_PGTABLE_ARMV7S_SELFTEST=y
CONFIG_IOMMU_IO_PGTABLE_DART=y
# end of Generic IOMMU Pagetable Support

CONFIG_IOMMU_DEBUGFS=y
CONFIG_IOMMU_DEFAULT_DMA_STRICT=y
# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set
# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set
CONFIG_OF_IOMMU=y
CONFIG_OMAP_IOMMU=y
CONFIG_OMAP_IOMMU_DEBUG=y
CONFIG_ROCKCHIP_IOMMU=y
CONFIG_SUN50I_IOMMU=y
CONFIG_IPMMU_VMSA=y
CONFIG_APPLE_DART=y
CONFIG_ARM_SMMU=y
CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS=y
CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT=y
CONFIG_S390_CCW_IOMMU=y
CONFIG_S390_AP_IOMMU=y
CONFIG_MTK_IOMMU=y
CONFIG_QCOM_IOMMU=y
CONFIG_SPRD_IOMMU=y

#
# Remoteproc drivers
#
CONFIG_REMOTEPROC=y
CONFIG_REMOTEPROC_CDEV=y
CONFIG_INGENIC_VPU_RPROC=y
CONFIG_MTK_SCP=y
CONFIG_MESON_MX_AO_ARC_REMOTEPROC=y
CONFIG_RCAR_REMOTEPROC=y
# end of Remoteproc drivers

#
# Rpmsg drivers
#
CONFIG_RPMSG=y
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_CTRL=y
CONFIG_RPMSG_NS=y
CONFIG_RPMSG_MTK_SCP=y
CONFIG_RPMSG_QCOM_GLINK=y
CONFIG_RPMSG_QCOM_GLINK_RPM=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
CONFIG_RPMSG_QCOM_SMD=y
CONFIG_RPMSG_VIRTIO=y
# end of Rpmsg drivers

CONFIG_SOUNDWIRE=y

#
# SoundWire Devices
#
CONFIG_SOUNDWIRE_QCOM=y

#
# SOC (System On Chip) specific Drivers
#
CONFIG_OWL_PM_DOMAINS_HELPER=y
CONFIG_OWL_PM_DOMAINS=y

#
# Amlogic SoC drivers
#
CONFIG_MESON_CANVAS=y
CONFIG_MESON_CLK_MEASURE=y
CONFIG_MESON_GX_SOCINFO=y
CONFIG_MESON_GX_PM_DOMAINS=y
CONFIG_MESON_EE_PM_DOMAINS=y
CONFIG_MESON_MX_SOCINFO=y
# end of Amlogic SoC drivers

#
# Apple SoC drivers
#
CONFIG_APPLE_PMGR_PWRSTATE=y
CONFIG_APPLE_RTKIT=y
CONFIG_APPLE_SART=y
# end of Apple SoC drivers

#
# ASPEED SoC drivers
#
CONFIG_ASPEED_LPC_CTRL=y
CONFIG_ASPEED_LPC_SNOOP=y
CONFIG_ASPEED_UART_ROUTING=y
CONFIG_ASPEED_P2A_CTRL=y
CONFIG_ASPEED_SOCINFO=y
# end of ASPEED SoC drivers

CONFIG_AT91_SOC_ID=y
CONFIG_AT91_SOC_SFR=y

#
# Broadcom SoC drivers
#
CONFIG_BCM2835_POWER=y
CONFIG_SOC_BCM63XX=y
CONFIG_SOC_BRCMSTB=y
CONFIG_BCM63XX_POWER=y
CONFIG_BCM_PMB=y
# end of Broadcom SoC drivers

#
# NXP/Freescale QorIQ SoC drivers
#
CONFIG_QUICC_ENGINE=y
CONFIG_UCC_SLOW=y
CONFIG_UCC_FAST=y
CONFIG_UCC=y
CONFIG_QE_TDM=y
CONFIG_FSL_GUTS=y
CONFIG_DPAA2_CONSOLE=y
# end of NXP/Freescale QorIQ SoC drivers

#
# fujitsu SoC drivers
#
# end of fujitsu SoC drivers

#
# i.MX SoC drivers
#
CONFIG_IMX_GPCV2_PM_DOMAINS=y
CONFIG_SOC_IMX8M=y
CONFIG_SOC_IMX9=y
# end of i.MX SoC drivers

#
# IXP4xx SoC drivers
#
CONFIG_IXP4XX_QMGR=y
CONFIG_IXP4XX_NPE=y
# end of IXP4xx SoC drivers

#
# Enable LiteX SoC Builder specific drivers
#
CONFIG_LITEX=y
CONFIG_LITEX_SOC_CONTROLLER=y
# end of Enable LiteX SoC Builder specific drivers

#
# MediaTek SoC drivers
#
CONFIG_MTK_CMDQ=y
CONFIG_MTK_DEVAPC=y
CONFIG_MTK_INFRACFG=y
CONFIG_MTK_PMIC_WRAP=y
CONFIG_MTK_SCPSYS=y
CONFIG_MTK_SCPSYS_PM_DOMAINS=y
CONFIG_MTK_MMSYS=y
CONFIG_MTK_SVS=y
# end of MediaTek SoC drivers

CONFIG_POLARFIRE_SOC_SYS_CTRL=y

#
# Qualcomm SoC drivers
#
CONFIG_QCOM_AOSS_QMP=y
CONFIG_QCOM_COMMAND_DB=y
CONFIG_QCOM_GENI_SE=y
CONFIG_QCOM_GSBI=y
CONFIG_QCOM_LLCC=y
CONFIG_QCOM_PDR_HELPERS=y
CONFIG_QCOM_QMI_HELPERS=y
CONFIG_QCOM_RPMH=y
CONFIG_QCOM_RPMHPD=y
CONFIG_QCOM_RPMPD=y
CONFIG_QCOM_SMEM=y
CONFIG_QCOM_SMD_RPM=y
CONFIG_QCOM_SMEM_STATE=y
CONFIG_QCOM_SMP2P=y
CONFIG_QCOM_SMSM=y
CONFIG_QCOM_SOCINFO=y
CONFIG_QCOM_SPM=y
CONFIG_QCOM_STATS=y
CONFIG_QCOM_WCNSS_CTRL=y
CONFIG_QCOM_APR=y
CONFIG_QCOM_ICC_BWMON=y
# end of Qualcomm SoC drivers

CONFIG_SOC_RENESAS=y
CONFIG_RST_RCAR=y
CONFIG_SYSC_RCAR=y
CONFIG_SYSC_RCAR_GEN4=y
CONFIG_SYSC_R8A77995=y
CONFIG_SYSC_R8A7794=y
CONFIG_SYSC_R8A77990=y
CONFIG_SYSC_R8A7779=y
CONFIG_SYSC_R8A7790=y
CONFIG_SYSC_R8A7795=y
CONFIG_SYSC_R8A7791=y
CONFIG_SYSC_R8A77965=y
CONFIG_SYSC_R8A77960=y
CONFIG_SYSC_R8A77961=y
CONFIG_SYSC_R8A779F0=y
CONFIG_SYSC_R8A7792=y
CONFIG_SYSC_R8A77980=y
CONFIG_SYSC_R8A77970=y
CONFIG_SYSC_R8A779A0=y
CONFIG_SYSC_R8A779G0=y
CONFIG_SYSC_RMOBILE=y
CONFIG_SYSC_R8A77470=y
CONFIG_SYSC_R8A7745=y
CONFIG_SYSC_R8A7742=y
CONFIG_SYSC_R8A7743=y
CONFIG_SYSC_R8A774C0=y
CONFIG_SYSC_R8A774E1=y
CONFIG_SYSC_R8A774A1=y
CONFIG_SYSC_R8A774B1=y
CONFIG_ROCKCHIP_GRF=y
CONFIG_ROCKCHIP_IODOMAIN=y
CONFIG_ROCKCHIP_PM_DOMAINS=y
CONFIG_ROCKCHIP_DTPM=m
CONFIG_SOC_SAMSUNG=y
CONFIG_EXYNOS_ASV_ARM=y
CONFIG_EXYNOS_CHIPID=y
CONFIG_EXYNOS_USI=y
CONFIG_EXYNOS_PM_DOMAINS=y
CONFIG_EXYNOS_REGULATOR_COUPLER=y
CONFIG_SUNXI_SRAM=y
CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER=y
CONFIG_SOC_TEGRA30_VOLTAGE_COUPLER=y
CONFIG_SOC_TI=y
CONFIG_UX500_SOC_ID=y

#
# Xilinx SoC drivers
#
# end of Xilinx SoC drivers
# end of SOC (System On Chip) specific Drivers

CONFIG_PM_DEVFREQ=y

#
# DEVFREQ Governors
#
CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
CONFIG_DEVFREQ_GOV_PERFORMANCE=y
CONFIG_DEVFREQ_GOV_POWERSAVE=y
CONFIG_DEVFREQ_GOV_USERSPACE=y
CONFIG_DEVFREQ_GOV_PASSIVE=y

#
# DEVFREQ Drivers
#
CONFIG_ARM_EXYNOS_BUS_DEVFREQ=y
CONFIG_ARM_IMX_BUS_DEVFREQ=y
CONFIG_ARM_TEGRA_DEVFREQ=y
CONFIG_ARM_MEDIATEK_CCI_DEVFREQ=y
CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ=y
CONFIG_PM_DEVFREQ_EVENT=y
CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP=y
CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU=y
CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI=y
CONFIG_EXTCON=y

#
# Extcon Device Drivers
#
CONFIG_EXTCON_ADC_JACK=y
CONFIG_EXTCON_FSA9480=y
CONFIG_EXTCON_GPIO=y
CONFIG_EXTCON_MAX14577=y
CONFIG_EXTCON_MAX3355=y
CONFIG_EXTCON_MAX77693=y
CONFIG_EXTCON_MAX77843=y
CONFIG_EXTCON_MAX8997=y
CONFIG_EXTCON_PALMAS=y
CONFIG_EXTCON_PTN5150=y
CONFIG_EXTCON_QCOM_SPMI_MISC=y
CONFIG_EXTCON_RT8973A=y
CONFIG_EXTCON_SM5502=y
CONFIG_EXTCON_USB_GPIO=y
CONFIG_EXTCON_USBC_CROS_EC=y
CONFIG_EXTCON_USBC_TUSB320=y
CONFIG_MEMORY=y
CONFIG_DDR=y
CONFIG_ATMEL_SDRAMC=y
CONFIG_ATMEL_EBI=y
CONFIG_BRCMSTB_DPFE=y
CONFIG_BRCMSTB_MEMC=y
CONFIG_BT1_L2_CTL=y
CONFIG_TI_AEMIF=y
CONFIG_TI_EMIF=y
CONFIG_FPGA_DFL_EMIF=y
CONFIG_MVEBU_DEVBUS=y
CONFIG_FSL_CORENET_CF=y
CONFIG_FSL_IFC=y
CONFIG_JZ4780_NEMC=y
CONFIG_MTK_SMI=y
CONFIG_DA8XX_DDRCTL=y
CONFIG_RENESAS_RPCIF=y
CONFIG_STM32_FMC2_EBI=y
CONFIG_SAMSUNG_MC=y
CONFIG_EXYNOS5422_DMC=y
CONFIG_EXYNOS_SROM=y
CONFIG_TEGRA_MC=y
CONFIG_TEGRA20_EMC=y
CONFIG_TEGRA30_EMC=y
CONFIG_TEGRA124_EMC=y
CONFIG_TEGRA210_EMC_TABLE=y
CONFIG_TEGRA210_EMC=y
CONFIG_IIO=y
CONFIG_IIO_BUFFER=y
CONFIG_IIO_BUFFER_CB=y
CONFIG_IIO_BUFFER_DMA=y
CONFIG_IIO_BUFFER_DMAENGINE=y
CONFIG_IIO_BUFFER_HW_CONSUMER=y
CONFIG_IIO_KFIFO_BUF=y
CONFIG_IIO_TRIGGERED_BUFFER=y
CONFIG_IIO_CONFIGFS=y
CONFIG_IIO_TRIGGER=y
CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
CONFIG_IIO_SW_DEVICE=y
CONFIG_IIO_SW_TRIGGER=y
CONFIG_IIO_TRIGGERED_EVENT=y

#
# Accelerometers
#
CONFIG_ADIS16201=y
CONFIG_ADIS16209=y
CONFIG_ADXL313=y
CONFIG_ADXL313_I2C=y
CONFIG_ADXL313_SPI=y
CONFIG_ADXL355=y
CONFIG_ADXL355_I2C=y
CONFIG_ADXL355_SPI=y
CONFIG_ADXL367=y
CONFIG_ADXL367_SPI=y
CONFIG_ADXL367_I2C=y
CONFIG_ADXL372=y
CONFIG_ADXL372_SPI=y
CONFIG_ADXL372_I2C=y
CONFIG_BMA220=y
CONFIG_BMA400=y
CONFIG_BMA400_I2C=y
CONFIG_BMA400_SPI=y
CONFIG_BMC150_ACCEL=y
CONFIG_BMC150_ACCEL_I2C=y
CONFIG_BMC150_ACCEL_SPI=y
CONFIG_BMI088_ACCEL=y
CONFIG_BMI088_ACCEL_SPI=y
CONFIG_DA280=y
CONFIG_DA311=y
CONFIG_DMARD06=y
CONFIG_DMARD09=y
CONFIG_DMARD10=y
CONFIG_FXLS8962AF=y
CONFIG_FXLS8962AF_I2C=y
CONFIG_FXLS8962AF_SPI=y
CONFIG_HID_SENSOR_ACCEL_3D=y
CONFIG_IIO_CROS_EC_ACCEL_LEGACY=y
CONFIG_KXSD9=y
CONFIG_KXSD9_SPI=y
CONFIG_KXSD9_I2C=y
CONFIG_KXCJK1013=y
CONFIG_MC3230=y
CONFIG_MMA7455=y
CONFIG_MMA7455_I2C=y
CONFIG_MMA7455_SPI=y
CONFIG_MMA7660=y
CONFIG_MMA8452=y
CONFIG_MMA9551_CORE=y
CONFIG_MMA9551=y
CONFIG_MMA9553=y
CONFIG_MSA311=y
CONFIG_MXC4005=y
CONFIG_MXC6255=y
CONFIG_SCA3000=y
CONFIG_SCA3300=y
CONFIG_STK8312=y
CONFIG_STK8BA50=y
# end of Accelerometers

#
# Analog to digital converters
#
CONFIG_AD_SIGMA_DELTA=y
CONFIG_AD7091R5=y
CONFIG_AD7124=y
CONFIG_AD7192=y
CONFIG_AD7266=y
CONFIG_AD7280=y
CONFIG_AD7291=y
CONFIG_AD7292=y
CONFIG_AD7298=y
CONFIG_AD7476=y
CONFIG_AD7606=y
CONFIG_AD7606_IFACE_PARALLEL=y
CONFIG_AD7606_IFACE_SPI=y
CONFIG_AD7766=y
CONFIG_AD7768_1=y
CONFIG_AD7780=y
CONFIG_AD7791=y
CONFIG_AD7793=y
CONFIG_AD7887=y
CONFIG_AD7923=y
CONFIG_AD7949=y
CONFIG_AD799X=y
CONFIG_AD9467=y
CONFIG_ADI_AXI_ADC=y
CONFIG_ASPEED_ADC=y
CONFIG_AT91_ADC=y
CONFIG_AT91_SAMA5D2_ADC=y
CONFIG_AXP20X_ADC=y
CONFIG_AXP288_ADC=y
CONFIG_BCM_IPROC_ADC=y
CONFIG_BERLIN2_ADC=y
CONFIG_CC10001_ADC=y
CONFIG_CPCAP_ADC=y
CONFIG_DA9150_GPADC=y
CONFIG_DLN2_ADC=y
CONFIG_ENVELOPE_DETECTOR=y
CONFIG_EXYNOS_ADC=y
CONFIG_MXS_LRADC_ADC=y
CONFIG_FSL_MX25_ADC=y
CONFIG_HI8435=y
CONFIG_HX711=y
CONFIG_INGENIC_ADC=y
CONFIG_IMX7D_ADC=y
CONFIG_IMX8QXP_ADC=y
CONFIG_LP8788_ADC=y
CONFIG_LPC18XX_ADC=y
CONFIG_LPC32XX_ADC=y
CONFIG_LTC2471=y
CONFIG_LTC2485=y
CONFIG_LTC2496=y
CONFIG_LTC2497=y
CONFIG_MAX1027=y
CONFIG_MAX11100=y
CONFIG_MAX1118=y
CONFIG_MAX11205=y
CONFIG_MAX1241=y
CONFIG_MAX1363=y
CONFIG_MAX9611=y
CONFIG_MCP320X=y
CONFIG_MCP3422=y
CONFIG_MCP3911=y
CONFIG_MEDIATEK_MT6360_ADC=y
CONFIG_MEDIATEK_MT6577_AUXADC=y
CONFIG_MEN_Z188_ADC=y
CONFIG_MESON_SARADC=y
CONFIG_MP2629_ADC=y
CONFIG_NAU7802=y
CONFIG_NPCM_ADC=y
CONFIG_PALMAS_GPADC=y
CONFIG_QCOM_VADC_COMMON=y
CONFIG_QCOM_PM8XXX_XOADC=y
CONFIG_QCOM_SPMI_RRADC=y
CONFIG_QCOM_SPMI_IADC=y
CONFIG_QCOM_SPMI_VADC=y
CONFIG_QCOM_SPMI_ADC5=y
CONFIG_RCAR_GYRO_ADC=y
CONFIG_RN5T618_ADC=y
CONFIG_ROCKCHIP_SARADC=y
CONFIG_RICHTEK_RTQ6056=y
CONFIG_RZG2L_ADC=y
CONFIG_SC27XX_ADC=y
CONFIG_SPEAR_ADC=y
CONFIG_SD_ADC_MODULATOR=y
CONFIG_STM32_ADC_CORE=y
CONFIG_STM32_ADC=y
CONFIG_STM32_DFSDM_CORE=y
CONFIG_STM32_DFSDM_ADC=y
CONFIG_STMPE_ADC=y
CONFIG_TI_ADC081C=y
CONFIG_TI_ADC0832=y
CONFIG_TI_ADC084S021=y
CONFIG_TI_ADC12138=y
CONFIG_TI_ADC108S102=y
CONFIG_TI_ADC128S052=y
CONFIG_TI_ADC161S626=y
CONFIG_TI_ADS1015=y
CONFIG_TI_ADS7950=y
CONFIG_TI_ADS8344=y
CONFIG_TI_ADS8688=y
CONFIG_TI_ADS124S08=y
CONFIG_TI_ADS131E08=y
CONFIG_TI_AM335X_ADC=y
CONFIG_TI_TLC4541=y
CONFIG_TI_TSC2046=y
CONFIG_TWL4030_MADC=y
CONFIG_TWL6030_GPADC=y
CONFIG_VF610_ADC=y
CONFIG_VIPERBOARD_ADC=y
CONFIG_XILINX_XADC=y
CONFIG_XILINX_AMS=y
# end of Analog to digital converters

#
# Analog to digital and digital to analog converters
#
CONFIG_AD74413R=y
# end of Analog to digital and digital to analog converters

#
# Analog Front Ends
#
CONFIG_IIO_RESCALE=y
# end of Analog Front Ends

#
# Amplifiers
#
CONFIG_AD8366=y
CONFIG_ADA4250=y
CONFIG_HMC425=y
# end of Amplifiers

#
# Capacitance to digital converters
#
CONFIG_AD7150=y
CONFIG_AD7746=y
# end of Capacitance to digital converters

#
# Chemical Sensors
#
CONFIG_ATLAS_PH_SENSOR=y
CONFIG_ATLAS_EZO_SENSOR=y
CONFIG_BME680=y
CONFIG_BME680_I2C=y
CONFIG_BME680_SPI=y
CONFIG_CCS811=y
CONFIG_IAQCORE=y
CONFIG_PMS7003=y
CONFIG_SCD30_CORE=y
CONFIG_SCD30_I2C=y
CONFIG_SCD30_SERIAL=y
CONFIG_SCD4X=y
CONFIG_SENSIRION_SGP30=y
CONFIG_SENSIRION_SGP40=y
CONFIG_SPS30=y
CONFIG_SPS30_I2C=y
CONFIG_SPS30_SERIAL=y
CONFIG_SENSEAIR_SUNRISE_CO2=y
CONFIG_VZ89X=y
# end of Chemical Sensors

CONFIG_IIO_CROS_EC_SENSORS_CORE=y
CONFIG_IIO_CROS_EC_SENSORS=y
CONFIG_IIO_CROS_EC_SENSORS_LID_ANGLE=y

#
# Hid Sensor IIO Common
#
CONFIG_HID_SENSOR_IIO_COMMON=y
CONFIG_HID_SENSOR_IIO_TRIGGER=y
# end of Hid Sensor IIO Common

CONFIG_IIO_MS_SENSORS_I2C=y

#
# IIO SCMI Sensors
#
CONFIG_IIO_SCMI=y
# end of IIO SCMI Sensors

#
# SSP Sensor Common
#
CONFIG_IIO_SSP_SENSORS_COMMONS=y
CONFIG_IIO_SSP_SENSORHUB=y
# end of SSP Sensor Common

CONFIG_IIO_ST_SENSORS_I2C=y
CONFIG_IIO_ST_SENSORS_SPI=y
CONFIG_IIO_ST_SENSORS_CORE=y

#
# Digital to analog converters
#
CONFIG_AD3552R=y
CONFIG_AD5064=y
CONFIG_AD5360=y
CONFIG_AD5380=y
CONFIG_AD5421=y
CONFIG_AD5446=y
CONFIG_AD5449=y
CONFIG_AD5592R_BASE=y
CONFIG_AD5592R=y
CONFIG_AD5593R=y
CONFIG_AD5504=y
CONFIG_AD5624R_SPI=y
CONFIG_LTC2688=y
CONFIG_AD5686=y
CONFIG_AD5686_SPI=y
CONFIG_AD5696_I2C=y
CONFIG_AD5755=y
CONFIG_AD5758=y
CONFIG_AD5761=y
CONFIG_AD5764=y
CONFIG_AD5766=y
CONFIG_AD5770R=y
CONFIG_AD5791=y
CONFIG_AD7293=y
CONFIG_AD7303=y
CONFIG_AD8801=y
CONFIG_DPOT_DAC=y
CONFIG_DS4424=y
CONFIG_LPC18XX_DAC=y
CONFIG_LTC1660=y
CONFIG_LTC2632=y
CONFIG_M62332=y
CONFIG_MAX517=y
CONFIG_MAX5821=y
CONFIG_MCP4725=y
CONFIG_MCP4922=y
CONFIG_STM32_DAC=y
CONFIG_STM32_DAC_CORE=y
CONFIG_TI_DAC082S085=y
CONFIG_TI_DAC5571=y
CONFIG_TI_DAC7311=y
CONFIG_TI_DAC7612=y
CONFIG_VF610_DAC=y
# end of Digital to analog converters

#
# IIO dummy driver
#
CONFIG_IIO_DUMMY_EVGEN=y
CONFIG_IIO_SIMPLE_DUMMY=y
CONFIG_IIO_SIMPLE_DUMMY_EVENTS=y
CONFIG_IIO_SIMPLE_DUMMY_BUFFER=y
# end of IIO dummy driver

#
# Filters
#
CONFIG_ADMV8818=y
# end of Filters

#
# Frequency Synthesizers DDS/PLL
#

#
# Clock Generator/Distribution
#
CONFIG_AD9523=y
# end of Clock Generator/Distribution

#
# Phase-Locked Loop (PLL) frequency synthesizers
#
CONFIG_ADF4350=y
CONFIG_ADF4371=y
CONFIG_ADMV1013=y
CONFIG_ADMV1014=y
CONFIG_ADMV4420=y
CONFIG_ADRF6780=y
# end of Phase-Locked Loop (PLL) frequency synthesizers
# end of Frequency Synthesizers DDS/PLL

#
# Digital gyroscope sensors
#
CONFIG_ADIS16080=y
CONFIG_ADIS16130=y
CONFIG_ADIS16136=y
CONFIG_ADIS16260=y
CONFIG_ADXRS290=y
CONFIG_ADXRS450=y
CONFIG_BMG160=y
CONFIG_BMG160_I2C=y
CONFIG_BMG160_SPI=y
CONFIG_FXAS21002C=y
CONFIG_FXAS21002C_I2C=y
CONFIG_FXAS21002C_SPI=y
CONFIG_HID_SENSOR_GYRO_3D=y
CONFIG_MPU3050=y
CONFIG_MPU3050_I2C=y
CONFIG_IIO_ST_GYRO_3AXIS=y
CONFIG_IIO_ST_GYRO_I2C_3AXIS=y
CONFIG_IIO_ST_GYRO_SPI_3AXIS=y
CONFIG_ITG3200=y
# end of Digital gyroscope sensors

#
# Health Sensors
#

#
# Heart Rate Monitors
#
CONFIG_AFE4403=y
CONFIG_AFE4404=y
CONFIG_MAX30100=y
CONFIG_MAX30102=y
# end of Heart Rate Monitors
# end of Health Sensors

#
# Humidity sensors
#
CONFIG_AM2315=y
CONFIG_DHT11=y
CONFIG_HDC100X=y
CONFIG_HDC2010=y
CONFIG_HID_SENSOR_HUMIDITY=y
CONFIG_HTS221=y
CONFIG_HTS221_I2C=y
CONFIG_HTS221_SPI=y
CONFIG_HTU21=y
CONFIG_SI7005=y
CONFIG_SI7020=y
# end of Humidity sensors

#
# Inertial measurement units
#
CONFIG_ADIS16400=y
CONFIG_ADIS16460=y
CONFIG_ADIS16475=y
CONFIG_ADIS16480=y
CONFIG_BMI160=y
CONFIG_BMI160_I2C=y
CONFIG_BMI160_SPI=y
CONFIG_BOSCH_BNO055=y
CONFIG_BOSCH_BNO055_SERIAL=y
CONFIG_BOSCH_BNO055_I2C=y
CONFIG_FXOS8700=y
CONFIG_FXOS8700_I2C=y
CONFIG_FXOS8700_SPI=y
CONFIG_KMX61=y
CONFIG_INV_ICM42600=y
CONFIG_INV_ICM42600_I2C=y
CONFIG_INV_ICM42600_SPI=y
CONFIG_INV_MPU6050_IIO=y
CONFIG_INV_MPU6050_I2C=y
CONFIG_INV_MPU6050_SPI=y
CONFIG_IIO_ST_LSM6DSX=y
CONFIG_IIO_ST_LSM6DSX_I2C=y
CONFIG_IIO_ST_LSM6DSX_SPI=y
CONFIG_IIO_ST_LSM6DSX_I3C=y
# end of Inertial measurement units

CONFIG_IIO_ADIS_LIB=y
CONFIG_IIO_ADIS_LIB_BUFFER=y

#
# Light sensors
#
CONFIG_ADJD_S311=y
CONFIG_ADUX1020=y
CONFIG_AL3010=y
CONFIG_AL3320A=y
CONFIG_APDS9300=y
CONFIG_APDS9960=y
CONFIG_AS73211=y
CONFIG_BH1750=y
CONFIG_BH1780=y
CONFIG_CM32181=y
CONFIG_CM3232=y
CONFIG_CM3323=y
CONFIG_CM3605=y
CONFIG_CM36651=y
CONFIG_IIO_CROS_EC_LIGHT_PROX=y
CONFIG_GP2AP002=y
CONFIG_GP2AP020A00F=y
CONFIG_IQS621_ALS=y
CONFIG_SENSORS_ISL29018=y
CONFIG_SENSORS_ISL29028=y
CONFIG_ISL29125=y
CONFIG_HID_SENSOR_ALS=y
CONFIG_HID_SENSOR_PROX=y
CONFIG_JSA1212=y
CONFIG_RPR0521=y
CONFIG_SENSORS_LM3533=y
CONFIG_LTR501=y
CONFIG_LTRF216A=y
CONFIG_LV0104CS=y
CONFIG_MAX44000=y
CONFIG_MAX44009=y
CONFIG_NOA1305=y
CONFIG_OPT3001=y
CONFIG_PA12203001=y
CONFIG_SI1133=y
CONFIG_SI1145=y
CONFIG_STK3310=y
CONFIG_ST_UVIS25=y
CONFIG_ST_UVIS25_I2C=y
CONFIG_ST_UVIS25_SPI=y
CONFIG_TCS3414=y
CONFIG_TCS3472=y
CONFIG_SENSORS_TSL2563=y
CONFIG_TSL2583=y
CONFIG_TSL2591=y
CONFIG_TSL2772=y
CONFIG_TSL4531=y
CONFIG_US5182D=y
CONFIG_VCNL4000=y
CONFIG_VCNL4035=y
CONFIG_VEML6030=y
CONFIG_VEML6070=y
CONFIG_VL6180=y
CONFIG_ZOPT2201=y
# end of Light sensors

#
# Magnetometer sensors
#
CONFIG_AK8974=y
CONFIG_AK8975=y
CONFIG_AK09911=y
CONFIG_BMC150_MAGN=y
CONFIG_BMC150_MAGN_I2C=y
CONFIG_BMC150_MAGN_SPI=y
CONFIG_MAG3110=y
CONFIG_HID_SENSOR_MAGNETOMETER_3D=y
CONFIG_MMC35240=y
CONFIG_IIO_ST_MAGN_3AXIS=y
CONFIG_IIO_ST_MAGN_I2C_3AXIS=y
CONFIG_IIO_ST_MAGN_SPI_3AXIS=y
CONFIG_SENSORS_HMC5843=y
CONFIG_SENSORS_HMC5843_I2C=y
CONFIG_SENSORS_HMC5843_SPI=y
CONFIG_SENSORS_RM3100=y
CONFIG_SENSORS_RM3100_I2C=y
CONFIG_SENSORS_RM3100_SPI=y
CONFIG_YAMAHA_YAS530=y
# end of Magnetometer sensors

#
# Multiplexers
#
CONFIG_IIO_MUX=y
# end of Multiplexers

#
# Inclinometer sensors
#
CONFIG_HID_SENSOR_INCLINOMETER_3D=y
CONFIG_HID_SENSOR_DEVICE_ROTATION=y
# end of Inclinometer sensors

CONFIG_IIO_RESCALE_KUNIT_TEST=y
CONFIG_IIO_FORMAT_KUNIT_TEST=y

#
# Triggers - standalone
#
CONFIG_IIO_HRTIMER_TRIGGER=y
CONFIG_IIO_INTERRUPT_TRIGGER=y
CONFIG_IIO_STM32_LPTIMER_TRIGGER=y
CONFIG_IIO_STM32_TIMER_TRIGGER=y
CONFIG_IIO_TIGHTLOOP_TRIGGER=y
CONFIG_IIO_SYSFS_TRIGGER=y
# end of Triggers - standalone

#
# Linear and angular position sensors
#
CONFIG_IQS624_POS=y
CONFIG_HID_SENSOR_CUSTOM_INTEL_HINGE=y
# end of Linear and angular position sensors

#
# Digital potentiometers
#
CONFIG_AD5110=y
CONFIG_AD5272=y
CONFIG_DS1803=y
CONFIG_MAX5432=y
CONFIG_MAX5481=y
CONFIG_MAX5487=y
CONFIG_MCP4018=y
CONFIG_MCP4131=y
CONFIG_MCP4531=y
CONFIG_MCP41010=y
CONFIG_TPL0102=y
# end of Digital potentiometers

#
# Digital potentiostats
#
CONFIG_LMP91000=y
# end of Digital potentiostats

#
# Pressure sensors
#
CONFIG_ABP060MG=y
CONFIG_BMP280=y
CONFIG_BMP280_I2C=y
CONFIG_BMP280_SPI=y
CONFIG_IIO_CROS_EC_BARO=y
CONFIG_DLHL60D=y
CONFIG_DPS310=y
CONFIG_HID_SENSOR_PRESS=y
CONFIG_HP03=y
CONFIG_ICP10100=y
CONFIG_MPL115=y
CONFIG_MPL115_I2C=y
CONFIG_MPL115_SPI=y
CONFIG_MPL3115=y
CONFIG_MS5611=y
CONFIG_MS5611_I2C=y
CONFIG_MS5611_SPI=y
CONFIG_MS5637=y
CONFIG_IIO_ST_PRESS=y
CONFIG_IIO_ST_PRESS_I2C=y
CONFIG_IIO_ST_PRESS_SPI=y
CONFIG_T5403=y
CONFIG_HP206C=y
CONFIG_ZPA2326=y
CONFIG_ZPA2326_I2C=y
CONFIG_ZPA2326_SPI=y
# end of Pressure sensors

#
# Lightning sensors
#
CONFIG_AS3935=y
# end of Lightning sensors

#
# Proximity and distance sensors
#
CONFIG_CROS_EC_MKBP_PROXIMITY=y
CONFIG_ISL29501=y
CONFIG_LIDAR_LITE_V2=y
CONFIG_MB1232=y
CONFIG_PING=y
CONFIG_RFD77402=y
CONFIG_SRF04=y
CONFIG_SX_COMMON=y
CONFIG_SX9310=y
CONFIG_SX9324=y
CONFIG_SX9360=y
CONFIG_SX9500=y
CONFIG_SRF08=y
CONFIG_VCNL3020=y
CONFIG_VL53L0X_I2C=y
# end of Proximity and distance sensors

#
# Resolver to digital converters
#
CONFIG_AD2S90=y
CONFIG_AD2S1200=y
# end of Resolver to digital converters

#
# Temperature sensors
#
CONFIG_IQS620AT_TEMP=y
CONFIG_LTC2983=y
CONFIG_MAXIM_THERMOCOUPLE=y
CONFIG_HID_SENSOR_TEMP=y
CONFIG_MLX90614=y
CONFIG_MLX90632=y
CONFIG_TMP006=y
CONFIG_TMP007=y
CONFIG_TMP117=y
CONFIG_TSYS01=y
CONFIG_TSYS02D=y
CONFIG_MAX31856=y
CONFIG_MAX31865=y
# end of Temperature sensors

CONFIG_NTB=y
CONFIG_NTB_MSI=y
CONFIG_NTB_IDT=y
CONFIG_NTB_EPF=m
CONFIG_NTB_SWITCHTEC=y
CONFIG_NTB_PINGPONG=y
CONFIG_NTB_TOOL=y
CONFIG_NTB_PERF=y
CONFIG_NTB_MSI_TEST=y
CONFIG_NTB_TRANSPORT=y
CONFIG_PWM=y
CONFIG_PWM_SYSFS=y
CONFIG_PWM_DEBUG=y
CONFIG_PWM_ATMEL=y
CONFIG_PWM_ATMEL_HLCDC_PWM=y
CONFIG_PWM_ATMEL_TCB=y
CONFIG_PWM_BCM_IPROC=y
CONFIG_PWM_BCM_KONA=y
CONFIG_PWM_BCM2835=y
CONFIG_PWM_BERLIN=y
CONFIG_PWM_BRCMSTB=y
CONFIG_PWM_CLK=y
CONFIG_PWM_CLPS711X=y
CONFIG_PWM_CROS_EC=y
CONFIG_PWM_DWC=y
CONFIG_PWM_EP93XX=y
CONFIG_PWM_FSL_FTM=y
CONFIG_PWM_HIBVT=y
CONFIG_PWM_IMG=y
CONFIG_PWM_IMX1=y
CONFIG_PWM_IMX27=y
CONFIG_PWM_IMX_TPM=y
CONFIG_PWM_INTEL_LGM=y
CONFIG_PWM_IQS620A=y
CONFIG_PWM_JZ4740=y
CONFIG_PWM_KEEMBAY=y
CONFIG_PWM_LP3943=y
CONFIG_PWM_LPC18XX_SCT=y
CONFIG_PWM_LPC32XX=y
CONFIG_PWM_LPSS=y
CONFIG_PWM_LPSS_PCI=y
CONFIG_PWM_LPSS_PLATFORM=y
CONFIG_PWM_MESON=y
CONFIG_PWM_MTK_DISP=y
CONFIG_PWM_MEDIATEK=y
CONFIG_PWM_MXS=y
CONFIG_PWM_NTXEC=y
CONFIG_PWM_OMAP_DMTIMER=y
CONFIG_PWM_PCA9685=y
CONFIG_PWM_PXA=y
CONFIG_PWM_RASPBERRYPI_POE=y
CONFIG_PWM_RCAR=y
CONFIG_PWM_RENESAS_TPU=y
CONFIG_PWM_ROCKCHIP=y
CONFIG_PWM_SAMSUNG=y
CONFIG_PWM_SIFIVE=y
CONFIG_PWM_SL28CPLD=y
CONFIG_PWM_SPEAR=y
CONFIG_PWM_SPRD=y
CONFIG_PWM_STI=y
CONFIG_PWM_STM32=y
CONFIG_PWM_STM32_LP=y
CONFIG_PWM_STMPE=y
CONFIG_PWM_SUN4I=y
CONFIG_PWM_SUNPLUS=y
CONFIG_PWM_TEGRA=y
CONFIG_PWM_TIECAP=y
CONFIG_PWM_TIEHRPWM=y
CONFIG_PWM_TWL=y
CONFIG_PWM_TWL_LED=y
CONFIG_PWM_VISCONTI=y
CONFIG_PWM_VT8500=y

#
# IRQ chip support
#
CONFIG_AL_FIC=y
CONFIG_MADERA_IRQ=y
CONFIG_JCORE_AIC=y
CONFIG_RENESAS_INTC_IRQPIN=y
CONFIG_RENESAS_IRQC=y
CONFIG_RENESAS_RZA1_IRQC=y
CONFIG_RENESAS_RZG2L_IRQC=y
CONFIG_SL28CPLD_INTC=y
CONFIG_TS4800_IRQ=y
CONFIG_INGENIC_TCU_IRQ=y
CONFIG_IRQ_UNIPHIER_AIDET=y
CONFIG_MESON_IRQ_GPIO=y
CONFIG_IMX_IRQSTEER=y
CONFIG_IMX_INTMUX=y
CONFIG_IMX_MU_MSI=y
CONFIG_EXYNOS_IRQ_COMBINER=y
CONFIG_MST_IRQ=y
CONFIG_MCHP_EIC=y
CONFIG_SUNPLUS_SP7021_INTC=y
# end of IRQ chip support

CONFIG_IPACK_BUS=y
CONFIG_BOARD_TPCI200=y
CONFIG_SERIAL_IPOCTAL=y
CONFIG_RESET_CONTROLLER=y
CONFIG_RESET_A10SR=y
CONFIG_RESET_ATH79=y
CONFIG_RESET_AXS10X=y
CONFIG_RESET_BCM6345=y
CONFIG_RESET_BERLIN=y
CONFIG_RESET_BRCMSTB=y
CONFIG_RESET_BRCMSTB_RESCAL=y
CONFIG_RESET_HSDK=y
CONFIG_RESET_IMX7=y
CONFIG_RESET_INTEL_GW=y
CONFIG_RESET_K210=y
CONFIG_RESET_LANTIQ=y
CONFIG_RESET_LPC18XX=y
CONFIG_RESET_MCHP_SPARX5=y
CONFIG_RESET_MESON=y
CONFIG_RESET_MESON_AUDIO_ARB=y
CONFIG_RESET_NPCM=y
CONFIG_RESET_PISTACHIO=y
CONFIG_RESET_POLARFIRE_SOC=y
CONFIG_RESET_QCOM_AOSS=y
CONFIG_RESET_QCOM_PDC=y
CONFIG_RESET_RASPBERRYPI=y
CONFIG_RESET_RZG2L_USBPHY_CTRL=y
CONFIG_RESET_SCMI=y
CONFIG_RESET_SIMPLE=y
CONFIG_RESET_SOCFPGA=y
CONFIG_RESET_STARFIVE_JH7100=y
CONFIG_RESET_SUNPLUS=y
CONFIG_RESET_SUNXI=y
CONFIG_RESET_TI_SCI=y
CONFIG_RESET_TI_SYSCON=y
CONFIG_RESET_TI_TPS380X=y
CONFIG_RESET_TN48M_CPLD=y
CONFIG_RESET_UNIPHIER=y
CONFIG_RESET_UNIPHIER_GLUE=y
CONFIG_RESET_ZYNQ=y
CONFIG_COMMON_RESET_HI3660=y
CONFIG_COMMON_RESET_HI6220=y

#
# PHY Subsystem
#
CONFIG_GENERIC_PHY=y
CONFIG_GENERIC_PHY_MIPI_DPHY=y
CONFIG_PHY_LPC18XX_USB_OTG=y
CONFIG_PHY_PISTACHIO_USB=y
CONFIG_PHY_XGENE=y
CONFIG_USB_LGM_PHY=y
CONFIG_PHY_CAN_TRANSCEIVER=y
CONFIG_PHY_SUN4I_USB=y
CONFIG_PHY_SUN6I_MIPI_DPHY=y
CONFIG_PHY_SUN9I_USB=y
CONFIG_PHY_SUN50I_USB3=y
CONFIG_PHY_MESON8_HDMI_TX=y
CONFIG_PHY_MESON8B_USB2=y
CONFIG_PHY_MESON_GXL_USB2=y
CONFIG_PHY_MESON_G12A_MIPI_DPHY_ANALOG=y
CONFIG_PHY_MESON_G12A_USB2=y
CONFIG_PHY_MESON_G12A_USB3_PCIE=y
CONFIG_PHY_MESON_AXG_PCIE=y
CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG=y
CONFIG_PHY_MESON_AXG_MIPI_DPHY=y

#
# PHY drivers for Broadcom platforms
#
CONFIG_PHY_BCM63XX_USBH=y
CONFIG_PHY_CYGNUS_PCIE=y
CONFIG_PHY_BCM_SR_USB=y
CONFIG_BCM_KONA_USB2_PHY=y
CONFIG_PHY_BCM_NS_USB2=y
CONFIG_PHY_BCM_NS_USB3=y
CONFIG_PHY_NS2_PCIE=y
CONFIG_PHY_NS2_USB_DRD=y
CONFIG_PHY_BRCM_SATA=y
CONFIG_PHY_BRCM_USB=y
CONFIG_PHY_BCM_SR_PCIE=y
# end of PHY drivers for Broadcom platforms

CONFIG_PHY_CADENCE_TORRENT=y
CONFIG_PHY_CADENCE_DPHY=y
CONFIG_PHY_CADENCE_DPHY_RX=y
CONFIG_PHY_CADENCE_SIERRA=y
CONFIG_PHY_CADENCE_SALVO=y
CONFIG_PHY_FSL_IMX8MQ_USB=y
CONFIG_PHY_MIXEL_LVDS_PHY=y
CONFIG_PHY_MIXEL_MIPI_DPHY=y
CONFIG_PHY_FSL_IMX8M_PCIE=y
CONFIG_PHY_FSL_LYNX_28G=y
CONFIG_PHY_HI6220_USB=y
CONFIG_PHY_HI3660_USB=y
CONFIG_PHY_HI3670_USB=y
CONFIG_PHY_HI3670_PCIE=y
CONFIG_PHY_HISTB_COMBPHY=y
CONFIG_PHY_HISI_INNO_USB2=y
CONFIG_PHY_INGENIC_USB=y
CONFIG_PHY_LANTIQ_VRX200_PCIE=y
CONFIG_PHY_LANTIQ_RCU_USB2=y
CONFIG_ARMADA375_USBCLUSTER_PHY=y
CONFIG_PHY_BERLIN_SATA=y
CONFIG_PHY_BERLIN_USB=y
CONFIG_PHY_MVEBU_A3700_UTMI=y
CONFIG_PHY_MVEBU_A38X_COMPHY=y
CONFIG_PHY_MVEBU_CP110_UTMI=y
CONFIG_PHY_PXA_28NM_HSIC=y
CONFIG_PHY_PXA_28NM_USB2=y
CONFIG_PHY_PXA_USB=y
CONFIG_PHY_MMP3_USB=y
CONFIG_PHY_MMP3_HSIC=y
CONFIG_PHY_MTK_PCIE=y
CONFIG_PHY_MTK_UFS=y
CONFIG_PHY_MTK_HDMI=y
CONFIG_PHY_MTK_MIPI_DSI=y
CONFIG_PHY_MTK_DP=y
CONFIG_PHY_SPARX5_SERDES=y
CONFIG_PHY_LAN966X_SERDES=y
CONFIG_PHY_CPCAP_USB=y
CONFIG_PHY_MAPPHONE_MDM6600=y
CONFIG_PHY_OCELOT_SERDES=y
CONFIG_PHY_ATH79_USB=y
CONFIG_PHY_QCOM_EDP=y
CONFIG_PHY_QCOM_IPQ4019_USB=y
CONFIG_PHY_QCOM_PCIE2=y
CONFIG_PHY_QCOM_QMP=y
CONFIG_PHY_QCOM_QUSB2=y
CONFIG_PHY_QCOM_USB_HS=y
CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y
CONFIG_PHY_QCOM_USB_HSIC=y
CONFIG_PHY_QCOM_USB_HS_28NM=y
CONFIG_PHY_QCOM_USB_SS=y
CONFIG_PHY_QCOM_IPQ806X_USB=y
CONFIG_PHY_MT7621_PCI=y
CONFIG_PHY_RALINK_USB=y
CONFIG_PHY_RCAR_GEN3_USB3=y
CONFIG_PHY_ROCKCHIP_DPHY_RX0=y
CONFIG_PHY_ROCKCHIP_INNO_HDMI=y
CONFIG_PHY_ROCKCHIP_INNO_USB2=y
CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY=y
CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=y
CONFIG_PHY_ROCKCHIP_PCIE=y
CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y
CONFIG_PHY_ROCKCHIP_TYPEC=y
CONFIG_PHY_EXYNOS_DP_VIDEO=y
CONFIG_PHY_EXYNOS_MIPI_VIDEO=y
CONFIG_PHY_EXYNOS_PCIE=y
CONFIG_PHY_SAMSUNG_UFS=y
CONFIG_PHY_SAMSUNG_USB2=y
CONFIG_PHY_S5PV210_USB2=y
CONFIG_PHY_EXYNOS5_USBDRD=y
CONFIG_PHY_UNIPHIER_USB2=y
CONFIG_PHY_UNIPHIER_USB3=y
CONFIG_PHY_UNIPHIER_PCIE=y
CONFIG_PHY_UNIPHIER_AHCI=y
CONFIG_PHY_ST_SPEAR1310_MIPHY=y
CONFIG_PHY_ST_SPEAR1340_MIPHY=y
CONFIG_PHY_STIH407_USB=y
CONFIG_PHY_STM32_USBPHYC=y
CONFIG_PHY_SUNPLUS_USB=y
CONFIG_PHY_TEGRA194_P2U=y
CONFIG_PHY_DA8XX_USB=y
CONFIG_PHY_DM816X_USB=y
CONFIG_PHY_AM654_SERDES=y
CONFIG_OMAP_CONTROL_PHY=y
CONFIG_TI_PIPE3=y
CONFIG_PHY_TUSB1210=y
CONFIG_PHY_TI_GMII_SEL=y
CONFIG_PHY_INTEL_KEEMBAY_EMMC=y
CONFIG_PHY_INTEL_KEEMBAY_USB=y
CONFIG_PHY_INTEL_LGM_COMBO=y
CONFIG_PHY_INTEL_LGM_EMMC=y
CONFIG_PHY_INTEL_THUNDERBAY_EMMC=y
CONFIG_PHY_XILINX_ZYNQMP=y
# end of PHY Subsystem

CONFIG_POWERCAP=y
CONFIG_DTPM=y
CONFIG_DTPM_CPU=y
CONFIG_DTPM_DEVFREQ=y
CONFIG_MCB=y
CONFIG_MCB_PCI=y
CONFIG_MCB_LPC=y

#
# Performance monitor support
#
CONFIG_ARM_CCN=y
CONFIG_ARM_CMN=y
CONFIG_ARM_SMMU_V3_PMU=y
CONFIG_FSL_IMX8_DDR_PMU=y
CONFIG_XGENE_PMU=y
CONFIG_ARM_DMC620_PMU=y
CONFIG_MARVELL_CN10K_TAD_PMU=y
CONFIG_ALIBABA_UNCORE_DRW_PMU=y
CONFIG_HNS3_PMU=y
CONFIG_MARVELL_CN10K_DDR_PMU=y
# end of Performance monitor support

CONFIG_RAS=y
CONFIG_USB4=y
CONFIG_USB4_DEBUGFS_WRITE=y
CONFIG_USB4_DEBUGFS_MARGINING=y
CONFIG_USB4_KUNIT_TEST=y
CONFIG_USB4_DMA_TEST=y

#
# Android
#
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_BINDERFS=y
CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder"
CONFIG_ANDROID_BINDER_IPC_SELFTEST=y
# end of Android

CONFIG_LIBNVDIMM=y
CONFIG_BLK_DEV_PMEM=y
CONFIG_ND_CLAIM=y
CONFIG_ND_BTT=y
CONFIG_BTT=y
CONFIG_OF_PMEM=y
CONFIG_NVDIMM_KEYS=y
CONFIG_DAX=y
CONFIG_DEV_DAX=y
CONFIG_NVMEM=y
CONFIG_NVMEM_SYSFS=y
CONFIG_NVMEM_APPLE_EFUSES=y
CONFIG_NVMEM_BCM_OCOTP=y
CONFIG_NVMEM_BRCM_NVRAM=y
CONFIG_NVMEM_IMX_IIM=y
CONFIG_NVMEM_IMX_OCOTP=y
CONFIG_NVMEM_JZ4780_EFUSE=y
CONFIG_NVMEM_LAN9662_OTPC=y
CONFIG_NVMEM_LAYERSCAPE_SFP=y
CONFIG_NVMEM_LPC18XX_EEPROM=y
CONFIG_NVMEM_LPC18XX_OTP=y
CONFIG_NVMEM_MESON_MX_EFUSE=y
CONFIG_NVMEM_MICROCHIP_OTPC=y
CONFIG_NVMEM_MTK_EFUSE=y
CONFIG_NVMEM_MXS_OCOTP=y
CONFIG_NVMEM_NINTENDO_OTP=y
CONFIG_NVMEM_QCOM_QFPROM=y
CONFIG_NVMEM_RAVE_SP_EEPROM=y
CONFIG_NVMEM_RMEM=y
CONFIG_NVMEM_ROCKCHIP_EFUSE=y
CONFIG_NVMEM_ROCKCHIP_OTP=y
CONFIG_NVMEM_SC27XX_EFUSE=y
CONFIG_NVMEM_SNVS_LPGPR=y
CONFIG_NVMEM_SPMI_SDAM=y
CONFIG_NVMEM_SPRD_EFUSE=y
CONFIG_NVMEM_STM32_ROMEM=y
CONFIG_NVMEM_SUNPLUS_OCOTP=y
CONFIG_NVMEM_U_BOOT_ENV=y
CONFIG_NVMEM_UNIPHIER_EFUSE=y
CONFIG_NVMEM_VF610_OCOTP=y

#
# HW tracing support
#
CONFIG_STM=y
CONFIG_STM_PROTO_BASIC=y
CONFIG_STM_PROTO_SYS_T=y
CONFIG_STM_DUMMY=y
CONFIG_STM_SOURCE_CONSOLE=y
CONFIG_STM_SOURCE_HEARTBEAT=y
CONFIG_STM_SOURCE_FTRACE=y
CONFIG_INTEL_TH=y
CONFIG_INTEL_TH_PCI=y
CONFIG_INTEL_TH_GTH=y
CONFIG_INTEL_TH_STH=y
CONFIG_INTEL_TH_MSU=y
CONFIG_INTEL_TH_PTI=y
CONFIG_INTEL_TH_DEBUG=y
CONFIG_HISI_PTT=y
# end of HW tracing support

CONFIG_FPGA=y
CONFIG_FPGA_MGR_SOCFPGA=y
CONFIG_FPGA_MGR_SOCFPGA_A10=y
CONFIG_ALTERA_PR_IP_CORE=y
CONFIG_ALTERA_PR_IP_CORE_PLAT=y
CONFIG_FPGA_MGR_ALTERA_PS_SPI=y
CONFIG_FPGA_MGR_ALTERA_CVP=y
CONFIG_FPGA_MGR_ZYNQ_FPGA=y
CONFIG_FPGA_MGR_XILINX_SPI=y
CONFIG_FPGA_MGR_ICE40_SPI=y
CONFIG_FPGA_MGR_MACHXO2_SPI=y
CONFIG_FPGA_BRIDGE=y
CONFIG_ALTERA_FREEZE_BRIDGE=y
CONFIG_XILINX_PR_DECOUPLER=y
CONFIG_FPGA_REGION=y
CONFIG_OF_FPGA_REGION=y
CONFIG_FPGA_DFL=y
CONFIG_FPGA_DFL_FME=y
CONFIG_FPGA_DFL_FME_MGR=y
CONFIG_FPGA_DFL_FME_BRIDGE=y
CONFIG_FPGA_DFL_FME_REGION=y
CONFIG_FPGA_DFL_AFU=y
CONFIG_FPGA_DFL_NIOS_INTEL_PAC_N3000=y
CONFIG_FPGA_DFL_PCI=y
CONFIG_FPGA_MGR_ZYNQMP_FPGA=y
CONFIG_FPGA_MGR_VERSAL_FPGA=y
CONFIG_FPGA_M10_BMC_SEC_UPDATE=y
CONFIG_FPGA_MGR_MICROCHIP_SPI=y
CONFIG_FSI=y
CONFIG_FSI_NEW_DEV_NODE=y
CONFIG_FSI_MASTER_GPIO=y
CONFIG_FSI_MASTER_HUB=y
CONFIG_FSI_MASTER_AST_CF=y
CONFIG_FSI_MASTER_ASPEED=y
CONFIG_FSI_SCOM=y
CONFIG_TEE=y
CONFIG_MULTIPLEXER=y

#
# Multiplexer drivers
#
CONFIG_MUX_ADG792A=y
CONFIG_MUX_ADGS1408=y
CONFIG_MUX_GPIO=y
CONFIG_MUX_MMIO=y
# end of Multiplexer drivers

CONFIG_PM_OPP=y
CONFIG_SIOX=y
CONFIG_SIOX_BUS_GPIO=y
CONFIG_SLIMBUS=y
CONFIG_SLIM_QCOM_CTRL=y
CONFIG_SLIM_QCOM_NGD_CTRL=y
CONFIG_INTERCONNECT=y
CONFIG_INTERCONNECT_IMX=y
CONFIG_INTERCONNECT_IMX8MM=y
CONFIG_INTERCONNECT_IMX8MN=y
CONFIG_INTERCONNECT_IMX8MQ=y
CONFIG_INTERCONNECT_IMX8MP=y
CONFIG_INTERCONNECT_QCOM_OSM_L3=y
CONFIG_INTERCONNECT_SAMSUNG=y
CONFIG_INTERCONNECT_EXYNOS=y
CONFIG_COUNTER=y
CONFIG_104_QUAD_8=y
CONFIG_INTERRUPT_CNT=y
CONFIG_STM32_TIMER_CNT=y
CONFIG_STM32_LPTIMER_CNT=y
CONFIG_TI_EQEP=y
CONFIG_FTM_QUADDEC=y
CONFIG_MICROCHIP_TCB_CAPTURE=y
CONFIG_INTEL_QEP=y
CONFIG_TI_ECAP_CAPTURE=y
CONFIG_MOST=y
CONFIG_MOST_USB_HDM=y
CONFIG_MOST_CDEV=y
CONFIG_MOST_SND=y
CONFIG_PECI=y
CONFIG_PECI_CPU=y
CONFIG_PECI_ASPEED=y
CONFIG_HTE=y
CONFIG_DPLL=y
# end of Device Drivers

#
# File systems
#
CONFIG_VALIDATE_FS_PARSER=y
CONFIG_FS_IOMAP=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
CONFIG_EXT2_FS_SECURITY=y
CONFIG_EXT3_FS=y
CONFIG_EXT3_FS_POSIX_ACL=y
CONFIG_EXT3_FS_SECURITY=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_DEBUG=y
CONFIG_EXT4_KUNIT_TESTS=y
CONFIG_JBD2=y
CONFIG_JBD2_DEBUG=y
CONFIG_FS_MBCACHE=y
CONFIG_REISERFS_FS=y
CONFIG_REISERFS_CHECK=y
CONFIG_REISERFS_PROC_INFO=y
CONFIG_REISERFS_FS_XATTR=y
CONFIG_REISERFS_FS_POSIX_ACL=y
CONFIG_REISERFS_FS_SECURITY=y
CONFIG_JFS_FS=y
CONFIG_JFS_POSIX_ACL=y
CONFIG_JFS_SECURITY=y
CONFIG_JFS_DEBUG=y
CONFIG_JFS_STATISTICS=y
CONFIG_XFS_FS=y
CONFIG_XFS_SUPPORT_V4=y
CONFIG_XFS_QUOTA=y
CONFIG_XFS_POSIX_ACL=y
CONFIG_XFS_RT=y
CONFIG_XFS_ONLINE_SCRUB=y
CONFIG_XFS_ONLINE_REPAIR=y
CONFIG_XFS_DEBUG=y
CONFIG_XFS_ASSERT_FATAL=y
CONFIG_GFS2_FS=y
CONFIG_GFS2_FS_LOCKING_DLM=y
CONFIG_OCFS2_FS=y
CONFIG_OCFS2_FS_O2CB=y
CONFIG_OCFS2_FS_USERSPACE_CLUSTER=y
CONFIG_OCFS2_FS_STATS=y
CONFIG_OCFS2_DEBUG_MASKLOG=y
CONFIG_OCFS2_DEBUG_FS=y
CONFIG_BTRFS_FS=y
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_BTRFS_FS_CHECK_INTEGRITY=y
CONFIG_BTRFS_FS_RUN_SANITY_TESTS=y
CONFIG_BTRFS_DEBUG=y
CONFIG_BTRFS_ASSERT=y
CONFIG_BTRFS_FS_REF_VERIFY=y
CONFIG_NILFS2_FS=y
CONFIG_F2FS_FS=y
CONFIG_F2FS_STAT_FS=y
CONFIG_F2FS_FS_XATTR=y
CONFIG_F2FS_FS_POSIX_ACL=y
CONFIG_F2FS_FS_SECURITY=y
CONFIG_F2FS_CHECK_FS=y
CONFIG_F2FS_FAULT_INJECTION=y
CONFIG_F2FS_FS_COMPRESSION=y
CONFIG_F2FS_FS_LZO=y
CONFIG_F2FS_FS_LZORLE=y
CONFIG_F2FS_FS_LZ4=y
CONFIG_F2FS_FS_LZ4HC=y
CONFIG_F2FS_FS_ZSTD=y
CONFIG_F2FS_IOSTAT=y
CONFIG_F2FS_UNFAIR_RWSEM=y
CONFIG_ZONEFS_FS=y
CONFIG_FS_POSIX_ACL=y
CONFIG_EXPORTFS=y
CONFIG_EXPORTFS_BLOCK_OPS=y
CONFIG_FILE_LOCKING=y
CONFIG_FS_ENCRYPTION=y
CONFIG_FS_ENCRYPTION_ALGS=y
CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y
CONFIG_FS_VERITY=y
CONFIG_FS_VERITY_DEBUG=y
CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y
CONFIG_FSNOTIFY=y
CONFIG_DNOTIFY=y
CONFIG_INOTIFY_USER=y
CONFIG_FANOTIFY=y
CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_PRINT_QUOTA_WARNING=y
CONFIG_QUOTA_DEBUG=y
CONFIG_QUOTA_TREE=y
CONFIG_QFMT_V1=y
CONFIG_QFMT_V2=y
CONFIG_QUOTACTL=y
CONFIG_AUTOFS4_FS=y
CONFIG_AUTOFS_FS=y
CONFIG_FUSE_FS=y
CONFIG_CUSE=y
CONFIG_VIRTIO_FS=y
CONFIG_OVERLAY_FS=y
CONFIG_OVERLAY_FS_REDIRECT_DIR=y
CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y
CONFIG_OVERLAY_FS_INDEX=y
CONFIG_OVERLAY_FS_XINO_AUTO=y
CONFIG_OVERLAY_FS_METACOPY=y

#
# Caches
#
CONFIG_NETFS_SUPPORT=y
CONFIG_NETFS_STATS=y
CONFIG_FSCACHE=y
CONFIG_FSCACHE_STATS=y
CONFIG_FSCACHE_DEBUG=y
CONFIG_CACHEFILES=y
CONFIG_CACHEFILES_DEBUG=y
CONFIG_CACHEFILES_ERROR_INJECTION=y
CONFIG_CACHEFILES_ONDEMAND=y
# end of Caches

#
# CD-ROM/DVD Filesystems
#
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
CONFIG_ZISOFS=y
CONFIG_UDF_FS=y
# end of CD-ROM/DVD Filesystems

#
# DOS/FAT/EXFAT/NT Filesystems
#
CONFIG_FAT_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
CONFIG_FAT_DEFAULT_UTF8=y
CONFIG_FAT_KUNIT_TEST=y
CONFIG_EXFAT_FS=y
CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8"
CONFIG_NTFS_FS=y
CONFIG_NTFS_DEBUG=y
CONFIG_NTFS_RW=y
CONFIG_NTFS3_FS=y
CONFIG_NTFS3_64BIT_CLUSTER=y
CONFIG_NTFS3_LZX_XPRESS=y
CONFIG_NTFS3_FS_POSIX_ACL=y
# end of DOS/FAT/EXFAT/NT Filesystems

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
CONFIG_PROC_SYSCTL=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_PROC_CHILDREN=y
CONFIG_KERNFS=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_TMPFS_XATTR=y
CONFIG_TMPFS_INODE64=y
CONFIG_HUGETLBFS=y
CONFIG_HUGETLB_PAGE=y
CONFIG_MEMFD_CREATE=y
CONFIG_ARCH_HAS_GIGANTIC_PAGE=y
CONFIG_CONFIGFS_FS=y
# end of Pseudo filesystems

CONFIG_MISC_FILESYSTEMS=y
CONFIG_ORANGEFS_FS=y
CONFIG_ADFS_FS=y
CONFIG_ADFS_FS_RW=y
CONFIG_AFFS_FS=y
CONFIG_ECRYPT_FS=y
CONFIG_ECRYPT_FS_MESSAGING=y
CONFIG_HFS_FS=y
CONFIG_HFSPLUS_FS=y
CONFIG_BEFS_FS=y
CONFIG_BEFS_DEBUG=y
CONFIG_BFS_FS=y
CONFIG_EFS_FS=y
CONFIG_JFFS2_FS=y
CONFIG_JFFS2_FS_DEBUG=0
CONFIG_JFFS2_FS_WRITEBUFFER=y
CONFIG_JFFS2_FS_WBUF_VERIFY=y
CONFIG_JFFS2_SUMMARY=y
CONFIG_JFFS2_FS_XATTR=y
CONFIG_JFFS2_FS_POSIX_ACL=y
CONFIG_JFFS2_FS_SECURITY=y
CONFIG_JFFS2_COMPRESSION_OPTIONS=y
CONFIG_JFFS2_ZLIB=y
CONFIG_JFFS2_LZO=y
CONFIG_JFFS2_RTIME=y
CONFIG_JFFS2_RUBIN=y
# CONFIG_JFFS2_CMODE_NONE is not set
CONFIG_JFFS2_CMODE_PRIORITY=y
# CONFIG_JFFS2_CMODE_SIZE is not set
# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
CONFIG_UBIFS_FS=y
CONFIG_UBIFS_FS_ADVANCED_COMPR=y
CONFIG_UBIFS_FS_LZO=y
CONFIG_UBIFS_FS_ZLIB=y
CONFIG_UBIFS_FS_ZSTD=y
CONFIG_UBIFS_ATIME_SUPPORT=y
CONFIG_UBIFS_FS_XATTR=y
CONFIG_UBIFS_FS_SECURITY=y
CONFIG_UBIFS_FS_AUTHENTICATION=y
CONFIG_CRAMFS=y
CONFIG_CRAMFS_BLOCKDEV=y
CONFIG_CRAMFS_MTD=y
CONFIG_SQUASHFS=y
CONFIG_SQUASHFS_FILE_CACHE=y
# CONFIG_SQUASHFS_FILE_DIRECT is not set
CONFIG_SQUASHFS_DECOMP_SINGLE=y
# CONFIG_SQUASHFS_DECOMP_MULTI is not set
# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set
CONFIG_SQUASHFS_XATTR=y
CONFIG_SQUASHFS_ZLIB=y
CONFIG_SQUASHFS_LZ4=y
CONFIG_SQUASHFS_LZO=y
CONFIG_SQUASHFS_XZ=y
CONFIG_SQUASHFS_ZSTD=y
CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y
CONFIG_SQUASHFS_EMBEDDED=y
CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
CONFIG_VXFS_FS=y
CONFIG_MINIX_FS=y
CONFIG_MINIX_FS_NATIVE_ENDIAN=y
CONFIG_OMFS_FS=y
CONFIG_HPFS_FS=y
CONFIG_QNX4FS_FS=y
CONFIG_QNX6FS_FS=y
CONFIG_QNX6FS_DEBUG=y
CONFIG_ROMFS_FS=y
CONFIG_ROMFS_BACKED_BY_BLOCK=y
# CONFIG_ROMFS_BACKED_BY_MTD is not set
# CONFIG_ROMFS_BACKED_BY_BOTH is not set
CONFIG_ROMFS_ON_BLOCK=y
CONFIG_PSTORE=y
CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240
CONFIG_PSTORE_DEFLATE_COMPRESS=y
CONFIG_PSTORE_LZO_COMPRESS=y
CONFIG_PSTORE_LZ4_COMPRESS=y
CONFIG_PSTORE_LZ4HC_COMPRESS=y
CONFIG_PSTORE_842_COMPRESS=y
CONFIG_PSTORE_ZSTD_COMPRESS=y
CONFIG_PSTORE_COMPRESS=y
CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT=y
# CONFIG_PSTORE_LZO_COMPRESS_DEFAULT is not set
# CONFIG_PSTORE_LZ4_COMPRESS_DEFAULT is not set
# CONFIG_PSTORE_LZ4HC_COMPRESS_DEFAULT is not set
# CONFIG_PSTORE_842_COMPRESS_DEFAULT is not set
# CONFIG_PSTORE_ZSTD_COMPRESS_DEFAULT is not set
CONFIG_PSTORE_COMPRESS_DEFAULT="deflate"
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_FTRACE=y
CONFIG_PSTORE_RAM=y
CONFIG_PSTORE_ZONE=y
CONFIG_PSTORE_BLK=y
CONFIG_PSTORE_BLK_BLKDEV=""
CONFIG_PSTORE_BLK_KMSG_SIZE=64
CONFIG_PSTORE_BLK_MAX_REASON=2
CONFIG_PSTORE_BLK_PMSG_SIZE=64
CONFIG_PSTORE_BLK_CONSOLE_SIZE=64
CONFIG_PSTORE_BLK_FTRACE_SIZE=64
CONFIG_SYSV_FS=y
CONFIG_UFS_FS=y
CONFIG_UFS_FS_WRITE=y
CONFIG_UFS_DEBUG=y
CONFIG_EROFS_FS=y
CONFIG_EROFS_FS_DEBUG=y
CONFIG_EROFS_FS_XATTR=y
CONFIG_EROFS_FS_POSIX_ACL=y
CONFIG_EROFS_FS_SECURITY=y
CONFIG_EROFS_FS_ZIP=y
CONFIG_EROFS_FS_ZIP_LZMA=y
CONFIG_EROFS_FS_ONDEMAND=y
CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V2=y
CONFIG_NFS_V3=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
CONFIG_NFS_SWAP=y
CONFIG_NFS_V4_1=y
CONFIG_NFS_V4_2=y
CONFIG_PNFS_FILE_LAYOUT=y
CONFIG_PNFS_BLOCK=y
CONFIG_PNFS_FLEXFILE_LAYOUT=y
CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org"
CONFIG_NFS_V4_1_MIGRATION=y
CONFIG_NFS_V4_SECURITY_LABEL=y
CONFIG_ROOT_NFS=y
CONFIG_NFS_FSCACHE=y
CONFIG_NFS_USE_LEGACY_DNS=y
CONFIG_NFS_DEBUG=y
CONFIG_NFS_DISABLE_UDP_SUPPORT=y
CONFIG_NFS_V4_2_READ_PLUS=y
CONFIG_NFSD=y
CONFIG_NFSD_V2_ACL=y
CONFIG_NFSD_V3_ACL=y
CONFIG_NFSD_V4=y
CONFIG_NFSD_PNFS=y
CONFIG_NFSD_BLOCKLAYOUT=y
CONFIG_NFSD_SCSILAYOUT=y
CONFIG_NFSD_FLEXFILELAYOUT=y
CONFIG_NFSD_V4_2_INTER_SSC=y
CONFIG_NFSD_V4_SECURITY_LABEL=y
CONFIG_GRACE_PERIOD=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_NFS_ACL_SUPPORT=y
CONFIG_NFS_COMMON=y
CONFIG_NFS_V4_2_SSC_HELPER=y
CONFIG_SUNRPC=y
CONFIG_SUNRPC_GSS=y
CONFIG_SUNRPC_BACKCHANNEL=y
CONFIG_SUNRPC_SWAP=y
CONFIG_RPCSEC_GSS_KRB5=y
CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES=y
CONFIG_SUNRPC_DEBUG=y
CONFIG_SUNRPC_XPRT_RDMA=y
CONFIG_CEPH_FS=y
CONFIG_CEPH_FSCACHE=y
CONFIG_CEPH_FS_POSIX_ACL=y
CONFIG_CEPH_FS_SECURITY_LABEL=y
CONFIG_CIFS=y
CONFIG_CIFS_STATS2=y
CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y
CONFIG_CIFS_UPCALL=y
CONFIG_CIFS_XATTR=y
CONFIG_CIFS_POSIX=y
CONFIG_CIFS_DEBUG=y
CONFIG_CIFS_DEBUG2=y
CONFIG_CIFS_DEBUG_DUMP_KEYS=y
CONFIG_CIFS_DFS_UPCALL=y
CONFIG_CIFS_SWN_UPCALL=y
CONFIG_CIFS_SMB_DIRECT=y
CONFIG_CIFS_FSCACHE=y
CONFIG_CIFS_ROOT=y
CONFIG_SMB_SERVER=y
CONFIG_SMB_SERVER_SMBDIRECT=y
CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN=y
CONFIG_SMB_SERVER_KERBEROS5=y
CONFIG_SMBFS_COMMON=y
CONFIG_CODA_FS=y
CONFIG_AFS_FS=y
CONFIG_AFS_DEBUG=y
CONFIG_AFS_FSCACHE=y
CONFIG_AFS_DEBUG_CURSOR=y
CONFIG_9P_FS=y
CONFIG_9P_FSCACHE=y
CONFIG_9P_FS_POSIX_ACL=y
CONFIG_9P_FS_SECURITY=y
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_CODEPAGE_737=y
CONFIG_NLS_CODEPAGE_775=y
CONFIG_NLS_CODEPAGE_850=y
CONFIG_NLS_CODEPAGE_852=y
CONFIG_NLS_CODEPAGE_855=y
CONFIG_NLS_CODEPAGE_857=y
CONFIG_NLS_CODEPAGE_860=y
CONFIG_NLS_CODEPAGE_861=y
CONFIG_NLS_CODEPAGE_862=y
CONFIG_NLS_CODEPAGE_863=y
CONFIG_NLS_CODEPAGE_864=y
CONFIG_NLS_CODEPAGE_865=y
CONFIG_NLS_CODEPAGE_866=y
CONFIG_NLS_CODEPAGE_869=y
CONFIG_NLS_CODEPAGE_936=y
CONFIG_NLS_CODEPAGE_950=y
CONFIG_NLS_CODEPAGE_932=y
CONFIG_NLS_CODEPAGE_949=y
CONFIG_NLS_CODEPAGE_874=y
CONFIG_NLS_ISO8859_8=y
CONFIG_NLS_CODEPAGE_1250=y
CONFIG_NLS_CODEPAGE_1251=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_ISO8859_2=y
CONFIG_NLS_ISO8859_3=y
CONFIG_NLS_ISO8859_4=y
CONFIG_NLS_ISO8859_5=y
CONFIG_NLS_ISO8859_6=y
CONFIG_NLS_ISO8859_7=y
CONFIG_NLS_ISO8859_9=y
CONFIG_NLS_ISO8859_13=y
CONFIG_NLS_ISO8859_14=y
CONFIG_NLS_ISO8859_15=y
CONFIG_NLS_KOI8_R=y
CONFIG_NLS_KOI8_U=y
CONFIG_NLS_MAC_ROMAN=y
CONFIG_NLS_MAC_CELTIC=y
CONFIG_NLS_MAC_CENTEURO=y
CONFIG_NLS_MAC_CROATIAN=y
CONFIG_NLS_MAC_CYRILLIC=y
CONFIG_NLS_MAC_GAELIC=y
CONFIG_NLS_MAC_GREEK=y
CONFIG_NLS_MAC_ICELAND=y
CONFIG_NLS_MAC_INUIT=y
CONFIG_NLS_MAC_ROMANIAN=y
CONFIG_NLS_MAC_TURKISH=y
CONFIG_NLS_UTF8=y
CONFIG_DLM=y
CONFIG_DLM_DEPRECATED_API=y
CONFIG_DLM_DEBUG=y
CONFIG_UNICODE=y
CONFIG_UNICODE_NORMALIZATION_SELFTEST=y
CONFIG_IO_WQ=y
# end of File systems

#
# Security options
#
CONFIG_KEYS=y
CONFIG_KEYS_REQUEST_CACHE=y
CONFIG_PERSISTENT_KEYRINGS=y
CONFIG_BIG_KEYS=y
CONFIG_TRUSTED_KEYS=y
CONFIG_TRUSTED_KEYS_TPM=y
CONFIG_TRUSTED_KEYS_TEE=y
CONFIG_ENCRYPTED_KEYS=y
CONFIG_USER_DECRYPTED_DATA=y
CONFIG_KEY_DH_OPERATIONS=y
CONFIG_KEY_NOTIFICATIONS=y
CONFIG_SECURITY_DMESG_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_SECURITY_WRITABLE_HOOKS=y
CONFIG_SECURITYFS=y
CONFIG_SECURITY_NETWORK=y
CONFIG_SECURITY_INFINIBAND=y
CONFIG_SECURITY_NETWORK_XFRM=y
CONFIG_SECURITY_PATH=y
CONFIG_LSM_MMAP_MIN_ADDR=65536
CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y
CONFIG_HARDENED_USERCOPY=y
CONFIG_STATIC_USERMODEHELPER=y
CONFIG_STATIC_USERMODEHELPER_PATH="/sbin/usermode-helper"
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SELINUX_BOOTPARAM=y
CONFIG_SECURITY_SELINUX_DISABLE=y
CONFIG_SECURITY_SELINUX_DEVELOP=y
CONFIG_SECURITY_SELINUX_AVC_STATS=y
CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=0
CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS=9
CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE=256
CONFIG_SECURITY_SMACK=y
CONFIG_SECURITY_SMACK_BRINGUP=y
CONFIG_SECURITY_SMACK_NETFILTER=y
CONFIG_SECURITY_SMACK_APPEND_SIGNALS=y
CONFIG_SECURITY_TOMOYO=y
CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY=2048
CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG=1024
CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER=y
CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING=y
CONFIG_SECURITY_APPARMOR=y
CONFIG_SECURITY_APPARMOR_DEBUG=y
CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS=y
CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES=y
CONFIG_SECURITY_APPARMOR_INTROSPECT_POLICY=y
CONFIG_SECURITY_APPARMOR_HASH=y
CONFIG_SECURITY_APPARMOR_HASH_DEFAULT=y
CONFIG_SECURITY_APPARMOR_EXPORT_BINARY=y
CONFIG_SECURITY_APPARMOR_PARANOID_LOAD=y
CONFIG_SECURITY_APPARMOR_KUNIT_TEST=y
CONFIG_SECURITY_LOADPIN=y
CONFIG_SECURITY_LOADPIN_ENFORCE=y
CONFIG_SECURITY_LOADPIN_VERITY=y
CONFIG_SECURITY_YAMA=y
CONFIG_SECURITY_SAFESETID=y
CONFIG_SECURITY_LOCKDOWN_LSM=y
CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y
CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y
# CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY is not set
# CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY is not set
CONFIG_SECURITY_LANDLOCK=y
CONFIG_INTEGRITY=y
CONFIG_INTEGRITY_SIGNATURE=y
CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y
CONFIG_INTEGRITY_TRUSTED_KEYRING=y
CONFIG_INTEGRITY_PLATFORM_KEYRING=y
CONFIG_INTEGRITY_AUDIT=y
CONFIG_IMA=y
CONFIG_IMA_MEASURE_PCR_IDX=10
CONFIG_IMA_LSM_RULES=y
CONFIG_IMA_NG_TEMPLATE=y
# CONFIG_IMA_SIG_TEMPLATE is not set
CONFIG_IMA_DEFAULT_TEMPLATE="ima-ng"
CONFIG_IMA_DEFAULT_HASH_SHA1=y
# CONFIG_IMA_DEFAULT_HASH_SHA256 is not set
# CONFIG_IMA_DEFAULT_HASH_SHA512 is not set
# CONFIG_IMA_DEFAULT_HASH_WP512 is not set
# CONFIG_IMA_DEFAULT_HASH_SM3 is not set
CONFIG_IMA_DEFAULT_HASH="sha1"
CONFIG_IMA_WRITE_POLICY=y
CONFIG_IMA_READ_POLICY=y
CONFIG_IMA_APPRAISE=y
CONFIG_IMA_ARCH_POLICY=y
CONFIG_IMA_APPRAISE_BUILD_POLICY=y
CONFIG_IMA_APPRAISE_REQUIRE_FIRMWARE_SIGS=y
CONFIG_IMA_APPRAISE_REQUIRE_KEXEC_SIGS=y
CONFIG_IMA_APPRAISE_REQUIRE_MODULE_SIGS=y
CONFIG_IMA_APPRAISE_REQUIRE_POLICY_SIGS=y
CONFIG_IMA_APPRAISE_BOOTPARAM=y
CONFIG_IMA_APPRAISE_MODSIG=y
CONFIG_IMA_TRUSTED_KEYRING=y
CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY=y
CONFIG_IMA_BLACKLIST_KEYRING=y
CONFIG_IMA_LOAD_X509=y
CONFIG_IMA_X509_PATH="/etc/keys/x509_ima.der"
CONFIG_IMA_APPRAISE_SIGNED_INIT=y
CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y
CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y
CONFIG_IMA_DISABLE_HTABLE=y
CONFIG_EVM=y
CONFIG_EVM_ATTR_FSUUID=y
CONFIG_EVM_EXTRA_SMACK_XATTRS=y
CONFIG_EVM_ADD_XATTRS=y
CONFIG_EVM_LOAD_X509=y
CONFIG_EVM_X509_PATH="/etc/keys/x509_evm.der"
CONFIG_DEFAULT_SECURITY_SELINUX=y
# CONFIG_DEFAULT_SECURITY_SMACK is not set
# CONFIG_DEFAULT_SECURITY_TOMOYO is not set
# CONFIG_DEFAULT_SECURITY_APPARMOR is not set
# CONFIG_DEFAULT_SECURITY_DAC is not set
CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"

#
# Kernel hardening options
#

#
# Memory initialization
#
CONFIG_CC_HAS_AUTO_VAR_INIT_PATTERN=y
CONFIG_CC_HAS_AUTO_VAR_INIT_ZERO_BARE=y
CONFIG_CC_HAS_AUTO_VAR_INIT_ZERO=y
# CONFIG_INIT_STACK_NONE is not set
CONFIG_INIT_STACK_ALL_PATTERN=y
# CONFIG_INIT_STACK_ALL_ZERO is not set
CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y
CONFIG_INIT_ON_FREE_DEFAULT_ON=y
CONFIG_CC_HAS_ZERO_CALL_USED_REGS=y
CONFIG_ZERO_CALL_USED_REGS=y
# end of Memory initialization

CONFIG_RANDSTRUCT_NONE=y
# end of Kernel hardening options
# end of Security options

CONFIG_XOR_BLOCKS=y
CONFIG_ASYNC_CORE=y
CONFIG_ASYNC_MEMCPY=y
CONFIG_ASYNC_XOR=y
CONFIG_ASYNC_PQ=y
CONFIG_ASYNC_RAID6_RECOV=y
CONFIG_ASYNC_TX_DISABLE_PQ_VAL_DMA=y
CONFIG_ASYNC_TX_DISABLE_XOR_VAL_DMA=y
CONFIG_CRYPTO=y

#
# Crypto core or helper
#
CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_ALGAPI2=y
CONFIG_CRYPTO_AEAD=y
CONFIG_CRYPTO_AEAD2=y
CONFIG_CRYPTO_SKCIPHER=y
CONFIG_CRYPTO_SKCIPHER2=y
CONFIG_CRYPTO_HASH=y
CONFIG_CRYPTO_HASH2=y
CONFIG_CRYPTO_RNG=y
CONFIG_CRYPTO_RNG2=y
CONFIG_CRYPTO_RNG_DEFAULT=y
CONFIG_CRYPTO_AKCIPHER2=y
CONFIG_CRYPTO_AKCIPHER=y
CONFIG_CRYPTO_KPP2=y
CONFIG_CRYPTO_KPP=y
CONFIG_CRYPTO_ACOMP2=y
CONFIG_CRYPTO_MANAGER=y
CONFIG_CRYPTO_MANAGER2=y
CONFIG_CRYPTO_USER=y
CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
CONFIG_CRYPTO_GF128MUL=y
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_NULL2=y
CONFIG_CRYPTO_PCRYPT=y
CONFIG_CRYPTO_CRYPTD=y
CONFIG_CRYPTO_AUTHENC=y
CONFIG_CRYPTO_TEST=y
CONFIG_CRYPTO_ENGINE=y
# end of Crypto core or helper

#
# Public-key cryptography
#
CONFIG_CRYPTO_RSA=y
CONFIG_CRYPTO_DH=y
CONFIG_CRYPTO_DH_RFC7919_GROUPS=y
CONFIG_CRYPTO_ECC=y
CONFIG_CRYPTO_ECDH=y
CONFIG_CRYPTO_ECDSA=y
CONFIG_CRYPTO_ECRDSA=y
CONFIG_CRYPTO_SM2=y
CONFIG_CRYPTO_CURVE25519=y
# end of Public-key cryptography

#
# Block ciphers
#
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_AES_TI=y
CONFIG_CRYPTO_ANUBIS=y
CONFIG_CRYPTO_ARIA=y
CONFIG_CRYPTO_BLOWFISH=y
CONFIG_CRYPTO_BLOWFISH_COMMON=y
CONFIG_CRYPTO_CAMELLIA=y
CONFIG_CRYPTO_CAST_COMMON=y
CONFIG_CRYPTO_CAST5=y
CONFIG_CRYPTO_CAST6=y
CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_FCRYPT=y
CONFIG_CRYPTO_KHAZAD=y
CONFIG_CRYPTO_SEED=y
CONFIG_CRYPTO_SERPENT=y
CONFIG_CRYPTO_SM4=y
CONFIG_CRYPTO_SM4_GENERIC=y
CONFIG_CRYPTO_TEA=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_TWOFISH_COMMON=y
# end of Block ciphers

#
# Length-preserving ciphers and modes
#
CONFIG_CRYPTO_ADIANTUM=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_CHACHA20=y
CONFIG_CRYPTO_CBC=y
CONFIG_CRYPTO_CFB=y
CONFIG_CRYPTO_CTR=y
CONFIG_CRYPTO_CTS=y
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_HCTR2=y
CONFIG_CRYPTO_KEYWRAP=y
CONFIG_CRYPTO_LRW=y
CONFIG_CRYPTO_OFB=y
CONFIG_CRYPTO_PCBC=y
CONFIG_CRYPTO_XCTR=y
CONFIG_CRYPTO_XTS=y
CONFIG_CRYPTO_NHPOLY1305=y
# end of Length-preserving ciphers and modes

#
# AEAD (authenticated encryption with associated data) ciphers
#
CONFIG_CRYPTO_AEGIS128=y
CONFIG_CRYPTO_CHACHA20POLY1305=y
CONFIG_CRYPTO_CCM=y
CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_SEQIV=y
CONFIG_CRYPTO_ECHAINIV=y
CONFIG_CRYPTO_ESSIV=y
# end of AEAD (authenticated encryption with associated data) ciphers

#
# Hashes, digests, and MACs
#
CONFIG_CRYPTO_BLAKE2B=y
CONFIG_CRYPTO_CMAC=y
CONFIG_CRYPTO_GHASH=y
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_MICHAEL_MIC=y
CONFIG_CRYPTO_POLYVAL=y
CONFIG_CRYPTO_POLY1305=y
CONFIG_CRYPTO_RMD160=y
CONFIG_CRYPTO_SHA1=y
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_SHA3=y
CONFIG_CRYPTO_SM3=y
CONFIG_CRYPTO_SM3_GENERIC=y
CONFIG_CRYPTO_STREEBOG=y
CONFIG_CRYPTO_VMAC=y
CONFIG_CRYPTO_WP512=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_XXHASH=y
# end of Hashes, digests, and MACs

#
# CRCs (cyclic redundancy checks)
#
CONFIG_CRYPTO_CRC32C=y
CONFIG_CRYPTO_CRC32=y
CONFIG_CRYPTO_CRCT10DIF=y
CONFIG_CRYPTO_CRC64_ROCKSOFT=y
# end of CRCs (cyclic redundancy checks)

#
# Compression
#
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
CONFIG_CRYPTO_842=y
CONFIG_CRYPTO_LZ4=y
CONFIG_CRYPTO_LZ4HC=y
CONFIG_CRYPTO_ZSTD=y
# end of Compression

#
# Random number generation
#
CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_DRBG_MENU=y
CONFIG_CRYPTO_DRBG_HMAC=y
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_DRBG=y
CONFIG_CRYPTO_JITTERENTROPY=y
CONFIG_CRYPTO_KDF800108_CTR=y
# end of Random number generation

#
# Userspace interface
#
CONFIG_CRYPTO_USER_API=y
CONFIG_CRYPTO_USER_API_HASH=y
CONFIG_CRYPTO_USER_API_SKCIPHER=y
CONFIG_CRYPTO_USER_API_RNG=y
CONFIG_CRYPTO_USER_API_RNG_CAVP=y
CONFIG_CRYPTO_USER_API_AEAD=y
CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y
CONFIG_CRYPTO_STATS=y
# end of Userspace interface

CONFIG_CRYPTO_HASH_INFO=y

#
# Accelerated Cryptographic Algorithms for CPU (sparc64)
#
CONFIG_CRYPTO_DES_SPARC64=y
CONFIG_CRYPTO_CRC32C_SPARC64=y
CONFIG_CRYPTO_MD5_SPARC64=y
CONFIG_CRYPTO_SHA1_SPARC64=y
CONFIG_CRYPTO_SHA256_SPARC64=y
CONFIG_CRYPTO_SHA512_SPARC64=y
CONFIG_CRYPTO_AES_SPARC64=y
CONFIG_CRYPTO_CAMELLIA_SPARC64=y
# end of Accelerated Cryptographic Algorithms for CPU (sparc64)

CONFIG_CRYPTO_HW=y
CONFIG_CRYPTO_DEV_ALLWINNER=y
CONFIG_CRYPTO_DEV_SUN8I_CE=y
CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG=y
CONFIG_CRYPTO_DEV_SUN8I_CE_HASH=y
CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG=y
CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG=y
CONFIG_CRYPTO_DEV_SUN8I_SS=y
CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG=y
CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG=y
CONFIG_CRYPTO_DEV_SUN8I_SS_HASH=y
CONFIG_CRYPTO_DEV_NIAGARA2=y
CONFIG_CRYPTO_DEV_SL3516=y
CONFIG_CRYPTO_DEV_SL3516_DEBUG=y
CONFIG_CRYPTO_DEV_EXYNOS_RNG=y
CONFIG_CRYPTO_DEV_S5P=y
CONFIG_CRYPTO_DEV_ATMEL_AUTHENC=y
CONFIG_CRYPTO_DEV_ATMEL_AES=y
CONFIG_CRYPTO_DEV_ATMEL_TDES=y
CONFIG_CRYPTO_DEV_ATMEL_SHA=y
CONFIG_CRYPTO_DEV_ATMEL_I2C=y
CONFIG_CRYPTO_DEV_ATMEL_ECC=y
CONFIG_CRYPTO_DEV_ATMEL_SHA204A=y
CONFIG_CRYPTO_DEV_QAT=y
CONFIG_CRYPTO_DEV_QAT_DH895xCC=y
CONFIG_CRYPTO_DEV_QAT_C3XXX=y
CONFIG_CRYPTO_DEV_QAT_C62X=y
CONFIG_CRYPTO_DEV_QAT_4XXX=y
CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=y
CONFIG_CRYPTO_DEV_QAT_C3XXXVF=y
CONFIG_CRYPTO_DEV_QAT_C62XVF=y
CONFIG_CRYPTO_DEV_CPT=y
CONFIG_CAVIUM_CPT=y
CONFIG_CRYPTO_DEV_NITROX=y
CONFIG_CRYPTO_DEV_NITROX_CNN55XX=y
CONFIG_CRYPTO_DEV_MARVELL=y
CONFIG_CRYPTO_DEV_OCTEONTX_CPT=y
CONFIG_CRYPTO_DEV_OCTEONTX2_CPT=y
CONFIG_CRYPTO_DEV_CAVIUM_ZIP=y
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y
CONFIG_CRYPTO_DEV_QCE_SHA=y
CONFIG_CRYPTO_DEV_QCE_AEAD=y
CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y
# CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set
# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set
# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set
CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512
CONFIG_CRYPTO_DEV_QCOM_RNG=y
CONFIG_CRYPTO_DEV_IMGTEC_HASH=y
CONFIG_CRYPTO_DEV_ZYNQMP_AES=y
CONFIG_CRYPTO_DEV_ZYNQMP_SHA3=y
CONFIG_CRYPTO_DEV_CHELSIO=y
CONFIG_CRYPTO_DEV_VIRTIO=y
CONFIG_CRYPTO_DEV_SAFEXCEL=y
CONFIG_CRYPTO_DEV_CCREE=y
CONFIG_CRYPTO_DEV_HISI_SEC=y
CONFIG_CRYPTO_DEV_AMLOGIC_GXL=y
CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG=y
CONFIG_CRYPTO_DEV_SA2UL=y
CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4=y
CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB=y
CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS=y
CONFIG_CRYPTO_DEV_KEEMBAY_OCS_ECC=y
CONFIG_CRYPTO_DEV_KEEMBAY_OCS_HCU=y
CONFIG_CRYPTO_DEV_KEEMBAY_OCS_HCU_HMAC_SHA224=y
CONFIG_CRYPTO_DEV_ASPEED=y
CONFIG_CRYPTO_DEV_ASPEED_DEBUG=y
CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH=y
CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO=y
CONFIG_ASYMMETRIC_KEY_TYPE=y
CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
CONFIG_X509_CERTIFICATE_PARSER=y
CONFIG_PKCS8_PRIVATE_KEY_PARSER=y
CONFIG_PKCS7_MESSAGE_PARSER=y
CONFIG_PKCS7_TEST_KEY=y
CONFIG_SIGNED_PE_FILE_VERIFICATION=y
CONFIG_FIPS_SIGNATURE_SELFTEST=y

#
# Certificates for signature checking
#
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
CONFIG_MODULE_SIG_KEY_TYPE_RSA=y
# CONFIG_MODULE_SIG_KEY_TYPE_ECDSA is not set
CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS=""
CONFIG_SYSTEM_EXTRA_CERTIFICATE=y
CONFIG_SYSTEM_EXTRA_CERTIFICATE_SIZE=4096
CONFIG_SECONDARY_TRUSTED_KEYRING=y
CONFIG_SYSTEM_BLACKLIST_KEYRING=y
CONFIG_SYSTEM_BLACKLIST_HASH_LIST=""
CONFIG_SYSTEM_REVOCATION_LIST=y
CONFIG_SYSTEM_REVOCATION_KEYS=""
CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE=y
# end of Certificates for signature checking

CONFIG_BINARY_PRINTF=y

#
# Library routines
#
CONFIG_RAID6_PQ=y
CONFIG_RAID6_PQ_BENCHMARK=y
CONFIG_LINEAR_RANGES=y
CONFIG_PACKING=y
CONFIG_BITREVERSE=y
CONFIG_GENERIC_STRNCPY_FROM_USER=y
CONFIG_GENERIC_STRNLEN_USER=y
CONFIG_GENERIC_NET_UTILS=y
CONFIG_CORDIC=y
CONFIG_PRIME_NUMBERS=y
CONFIG_RATIONAL=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_STMP_DEVICE=y

#
# Crypto library routines
#
CONFIG_CRYPTO_LIB_UTILS=y
CONFIG_CRYPTO_LIB_AES=y
CONFIG_CRYPTO_LIB_ARC4=y
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
CONFIG_CRYPTO_LIB_CHACHA_GENERIC=y
CONFIG_CRYPTO_LIB_CHACHA=y
CONFIG_CRYPTO_LIB_CURVE25519_GENERIC=y
CONFIG_CRYPTO_LIB_CURVE25519=y
CONFIG_CRYPTO_LIB_DES=y
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1
CONFIG_CRYPTO_LIB_POLY1305_GENERIC=y
CONFIG_CRYPTO_LIB_POLY1305=y
CONFIG_CRYPTO_LIB_CHACHA20POLY1305=y
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_SHA256=y
# end of Crypto library routines

CONFIG_CRC_CCITT=y
CONFIG_CRC16=y
CONFIG_CRC_T10DIF=y
CONFIG_CRC64_ROCKSOFT=y
CONFIG_CRC_ITU_T=y
CONFIG_CRC32=y
CONFIG_CRC32_SELFTEST=y
CONFIG_CRC32_SLICEBY8=y
# CONFIG_CRC32_SLICEBY4 is not set
# CONFIG_CRC32_SARWATE is not set
# CONFIG_CRC32_BIT is not set
CONFIG_CRC64=y
CONFIG_CRC4=y
CONFIG_CRC7=y
CONFIG_LIBCRC32C=y
CONFIG_CRC8=y
CONFIG_XXHASH=y
CONFIG_RANDOM32_SELFTEST=y
CONFIG_842_COMPRESS=y
CONFIG_842_DECOMPRESS=y
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_LZ4_COMPRESS=y
CONFIG_LZ4HC_COMPRESS=y
CONFIG_LZ4_DECOMPRESS=y
CONFIG_ZSTD_COMMON=y
CONFIG_ZSTD_COMPRESS=y
CONFIG_ZSTD_DECOMPRESS=y
CONFIG_XZ_DEC=y
CONFIG_XZ_DEC_X86=y
CONFIG_XZ_DEC_POWERPC=y
CONFIG_XZ_DEC_IA64=y
CONFIG_XZ_DEC_ARM=y
CONFIG_XZ_DEC_ARMTHUMB=y
CONFIG_XZ_DEC_SPARC=y
CONFIG_XZ_DEC_MICROLZMA=y
CONFIG_XZ_DEC_BCJ=y
CONFIG_XZ_DEC_TEST=y
CONFIG_DECOMPRESS_GZIP=y
CONFIG_DECOMPRESS_BZIP2=y
CONFIG_DECOMPRESS_LZMA=y
CONFIG_DECOMPRESS_XZ=y
CONFIG_DECOMPRESS_LZO=y
CONFIG_DECOMPRESS_LZ4=y
CONFIG_DECOMPRESS_ZSTD=y
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_REED_SOLOMON=y
CONFIG_REED_SOLOMON_ENC8=y
CONFIG_REED_SOLOMON_DEC8=y
CONFIG_REED_SOLOMON_ENC16=y
CONFIG_REED_SOLOMON_DEC16=y
CONFIG_BCH=y
CONFIG_TEXTSEARCH=y
CONFIG_TEXTSEARCH_KMP=y
CONFIG_TEXTSEARCH_BM=y
CONFIG_TEXTSEARCH_FSM=y
CONFIG_BTREE=y
CONFIG_INTERVAL_TREE=y
CONFIG_XARRAY_MULTI=y
CONFIG_ASSOCIATIVE_ARRAY=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_HAS_DMA=y
CONFIG_DMA_OPS=y
CONFIG_NEED_SG_DMA_LENGTH=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
CONFIG_DMA_DECLARE_COHERENT=y
CONFIG_DMA_API_DEBUG=y
CONFIG_DMA_API_DEBUG_SG=y
CONFIG_DMA_MAP_BENCHMARK=y
CONFIG_SGL_ALLOC=y
CONFIG_IOMMU_HELPER=y
CONFIG_CHECK_SIGNATURE=y
CONFIG_CPUMASK_OFFSTACK=y
CONFIG_FORCE_NR_CPUS=y
CONFIG_CPU_RMAP=y
CONFIG_DQL=y
CONFIG_GLOB=y
CONFIG_GLOB_SELFTEST=y
CONFIG_NLATTR=y
CONFIG_LRU_CACHE=y
CONFIG_CLZ_TAB=y
CONFIG_IRQ_POLL=y
CONFIG_MPILIB=y
CONFIG_SIGNATURE=y
CONFIG_DIMLIB=y
CONFIG_LIBFDT=y
CONFIG_OID_REGISTRY=y
CONFIG_FONT_SUPPORT=y
CONFIG_FONTS=y
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y
CONFIG_FONT_6x11=y
CONFIG_FONT_7x14=y
CONFIG_FONT_PEARL_8x8=y
CONFIG_FONT_ACORN_8x8=y
CONFIG_FONT_10x18=y
CONFIG_FONT_SUN8x16=y
CONFIG_FONT_SUN12x22=y
CONFIG_FONT_TER16x32=y
CONFIG_FONT_6x8=y
CONFIG_SG_SPLIT=y
CONFIG_SG_POOL=y
CONFIG_MEMREGION=y
CONFIG_STACKDEPOT=y
CONFIG_STACKDEPOT_ALWAYS_INIT=y
CONFIG_REF_TRACKER=y
CONFIG_SBITMAP=y
CONFIG_PARMAN=y
CONFIG_OBJAGG=y
# end of Library routines

CONFIG_PLDMFW=y
CONFIG_ASN1_ENCODER=y
CONFIG_POLYNOMIAL=y

#
# Kernel hacking
#

#
# printk and dmesg options
#
CONFIG_PRINTK_TIME=y
CONFIG_PRINTK_CALLER=y
CONFIG_STACKTRACE_BUILD_ID=y
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
CONFIG_CONSOLE_LOGLEVEL_QUIET=4
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
CONFIG_BOOT_PRINTK_DELAY=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DYNAMIC_DEBUG_CORE=y
CONFIG_SYMBOLIC_ERRNAME=y
CONFIG_DEBUG_BUGVERBOSE=y
# end of printk and dmesg options

CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MISC=y

#
# Compile-time checks and compiler options
#
CONFIG_AS_HAS_NON_CONST_LEB128=y
CONFIG_DEBUG_INFO_NONE=y
# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set
# CONFIG_DEBUG_INFO_DWARF4 is not set
# CONFIG_DEBUG_INFO_DWARF5 is not set
CONFIG_FRAME_WARN=2048
CONFIG_STRIP_ASM_SYMS=y
CONFIG_READABLE_ASM=y
CONFIG_HEADERS_INSTALL=y
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
CONFIG_FRAME_POINTER=y
CONFIG_VMLINUX_MAP=y
CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y
# end of Compile-time checks and compiler options

#
# Generic Kernel Debugging Instruments
#
CONFIG_MAGIC_SYSRQ=y
CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
CONFIG_MAGIC_SYSRQ_SERIAL=y
CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
CONFIG_DEBUG_FS=y
CONFIG_DEBUG_FS_ALLOW_ALL=y
# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
# CONFIG_DEBUG_FS_ALLOW_NONE is not set
CONFIG_HAVE_ARCH_KGDB=y
CONFIG_KGDB=y
CONFIG_KGDB_HONOUR_BLOCKLIST=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_KGDB_TESTS=y
CONFIG_KGDB_TESTS_ON_BOOT=y
CONFIG_KGDB_TESTS_BOOT_STRING="V1F100"
CONFIG_KGDB_KDB=y
CONFIG_KDB_DEFAULT_ENABLE=0x1
CONFIG_KDB_KEYBOARD=y
CONFIG_KDB_CONTINUE_CATASTROPHIC=0
CONFIG_UBSAN=y
CONFIG_CC_HAS_UBSAN_BOUNDS=y
CONFIG_UBSAN_BOUNDS=y
CONFIG_UBSAN_ONLY_BOUNDS=y
CONFIG_UBSAN_SHIFT=y
CONFIG_UBSAN_DIV_ZERO=y
CONFIG_UBSAN_UNREACHABLE=y
CONFIG_UBSAN_BOOL=y
CONFIG_UBSAN_ENUM=y
CONFIG_TEST_UBSAN=m
CONFIG_HAVE_KCSAN_COMPILER=y
# end of Generic Kernel Debugging Instruments

#
# Networking Debugging
#
CONFIG_NET_DEV_REFCNT_TRACKER=y
CONFIG_NET_NS_REFCNT_TRACKER=y
CONFIG_DEBUG_NET=y
# end of Networking Debugging

#
# Memory Debugging
#
CONFIG_PAGE_EXTENSION=y
CONFIG_SLUB_DEBUG=y
CONFIG_SLUB_DEBUG_ON=y
CONFIG_PAGE_OWNER=y
CONFIG_PAGE_POISONING=y
CONFIG_DEBUG_PAGE_REF=y
CONFIG_DEBUG_OBJECTS=y
CONFIG_DEBUG_OBJECTS_SELFTEST=y
CONFIG_DEBUG_OBJECTS_FREE=y
CONFIG_DEBUG_OBJECTS_TIMERS=y
CONFIG_DEBUG_OBJECTS_WORK=y
CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1
CONFIG_SHRINKER_DEBUG=y
CONFIG_HAVE_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE=16000
CONFIG_DEBUG_KMEMLEAK_TEST=m
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_SCHED_STACK_END_CHECK=y
CONFIG_DEBUG_VM_IRQSOFF=y
CONFIG_DEBUG_VM=y
CONFIG_DEBUG_VM_MAPLE_TREE=y
CONFIG_DEBUG_VM_RB=y
CONFIG_DEBUG_VM_PGFLAGS=y
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_DEBUG_PER_CPU_MAPS=y
CONFIG_CC_HAS_KASAN_GENERIC=y
CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y
# end of Memory Debugging

CONFIG_DEBUG_SHIRQ=y

#
# Debug Oops, Lockups and Hangs
#
CONFIG_PANIC_ON_OOPS=y
CONFIG_PANIC_ON_OOPS_VALUE=1
CONFIG_PANIC_TIMEOUT=0
CONFIG_LOCKUP_DETECTOR=y
CONFIG_SOFTLOCKUP_DETECTOR=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_TEST_LOCKUP=m
# end of Debug Oops, Lockups and Hangs

#
# Scheduler Debugging
#
CONFIG_SCHED_DEBUG=y
CONFIG_SCHED_INFO=y
CONFIG_SCHEDSTATS=y
# end of Scheduler Debugging

CONFIG_DEBUG_TIMEKEEPING=y

#
# Lock Debugging (spinlocks, mutexes, etc...)
#
CONFIG_LOCK_DEBUGGING_SUPPORT=y
CONFIG_PROVE_LOCKING=y
CONFIG_PROVE_RAW_LOCK_NESTING=y
CONFIG_LOCK_STAT=y
CONFIG_DEBUG_RT_MUTEXES=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
CONFIG_DEBUG_RWSEMS=y
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_LOCKDEP=y
CONFIG_LOCKDEP_SMALL=y
CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12
CONFIG_DEBUG_LOCKDEP=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_DEBUG_LOCKING_API_SELFTESTS=y
CONFIG_LOCK_TORTURE_TEST=y
CONFIG_WW_MUTEX_SELFTEST=y
CONFIG_SCF_TORTURE_TEST=y
CONFIG_CSD_LOCK_WAIT_DEBUG=y
# end of Lock Debugging (spinlocks, mutexes, etc...)

CONFIG_TRACE_IRQFLAGS=y
CONFIG_DEBUG_IRQFLAGS=y
CONFIG_STACKTRACE=y
CONFIG_WARN_ALL_UNSEEDED_RANDOM=y
CONFIG_DEBUG_KOBJECT=y
CONFIG_DEBUG_KOBJECT_RELEASE=y
CONFIG_HAVE_DEBUG_BUGVERBOSE=y

#
# Debug kernel data structures
#
CONFIG_DEBUG_LIST=y
CONFIG_DEBUG_PLIST=y
CONFIG_DEBUG_SG=y
CONFIG_DEBUG_NOTIFIERS=y
CONFIG_BUG_ON_DATA_CORRUPTION=y
CONFIG_DEBUG_MAPLE_TREE=y
# end of Debug kernel data structures

CONFIG_DEBUG_CREDENTIALS=y

#
# RCU Debugging
#
CONFIG_PROVE_RCU=y
CONFIG_PROVE_RCU_LIST=y
CONFIG_TORTURE_TEST=y
CONFIG_RCU_SCALE_TEST=y
CONFIG_RCU_TORTURE_TEST=y
CONFIG_RCU_REF_SCALE_TEST=y
CONFIG_RCU_CPU_STALL_TIMEOUT=21
CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=0
CONFIG_RCU_TRACE=y
CONFIG_RCU_EQS_DEBUG=y
# end of RCU Debugging

CONFIG_DEBUG_WQ_FORCE_RR_CPU=y
CONFIG_CPU_HOTPLUG_STATE_CONTROL=y
CONFIG_LATENCYTOP=y
CONFIG_NOP_TRACER=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_TRACER_MAX_TRACE=y
CONFIG_TRACE_CLOCK=y
CONFIG_RING_BUFFER=y
CONFIG_EVENT_TRACING=y
CONFIG_CONTEXT_SWITCH_TRACER=y
CONFIG_RING_BUFFER_ALLOW_SWAP=y
CONFIG_PREEMPTIRQ_TRACEPOINTS=y
CONFIG_TRACING=y
CONFIG_GENERIC_TRACER=y
CONFIG_TRACING_SUPPORT=y
CONFIG_FTRACE=y
CONFIG_BOOTTIME_TRACING=y
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_DYNAMIC_FTRACE=y
CONFIG_FUNCTION_PROFILER=y
CONFIG_STACK_TRACER=y
CONFIG_IRQSOFF_TRACER=y
CONFIG_SCHED_TRACER=y
CONFIG_HWLAT_TRACER=y
CONFIG_OSNOISE_TRACER=y
CONFIG_TIMERLAT_TRACER=y
CONFIG_FTRACE_SYSCALLS=y
CONFIG_TRACER_SNAPSHOT=y
CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP=y
CONFIG_BRANCH_PROFILE_NONE=y
# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
# CONFIG_PROFILE_ALL_BRANCHES is not set
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_KPROBE_EVENTS=y
CONFIG_KPROBE_EVENTS_ON_NOTRACE=y
CONFIG_UPROBE_EVENTS=y
CONFIG_BPF_EVENTS=y
CONFIG_DYNAMIC_EVENTS=y
CONFIG_PROBE_EVENTS=y
CONFIG_FTRACE_MCOUNT_RECORD=y
CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT=y
CONFIG_TRACING_MAP=y
CONFIG_SYNTH_EVENTS=y
CONFIG_USER_EVENTS=y
CONFIG_HIST_TRIGGERS=y
CONFIG_TRACE_EVENT_INJECT=y
CONFIG_TRACEPOINT_BENCHMARK=y
CONFIG_RING_BUFFER_BENCHMARK=y
CONFIG_TRACE_EVAL_MAP_FILE=y
CONFIG_FTRACE_RECORD_RECURSION=y
CONFIG_FTRACE_RECORD_RECURSION_SIZE=128
CONFIG_RING_BUFFER_RECORD_RECURSION=y
CONFIG_GCOV_PROFILE_FTRACE=y
CONFIG_FTRACE_SELFTEST=y
CONFIG_FTRACE_STARTUP_TEST=y
CONFIG_EVENT_TRACE_STARTUP_TEST=y
CONFIG_EVENT_TRACE_TEST_SYSCALLS=y
CONFIG_RING_BUFFER_STARTUP_TEST=y
CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS=y
CONFIG_PREEMPTIRQ_DELAY_TEST=m
CONFIG_SYNTH_EVENT_GEN_TEST=y
CONFIG_KPROBE_EVENT_GEN_TEST=y
CONFIG_HIST_TRIGGERS_DEBUG=y
CONFIG_DA_MON_EVENTS=y
CONFIG_DA_MON_EVENTS_ID=y
CONFIG_RV=y
CONFIG_RV_MON_WWNR=y
CONFIG_RV_REACTORS=y
CONFIG_RV_REACT_PRINTK=y
CONFIG_RV_REACT_PANIC=y
# CONFIG_SAMPLES is not set
# CONFIG_STRICT_DEVMEM is not set

#
# sparc Debugging
#
CONFIG_DEBUG_DCFLUSH=y
CONFIG_MCOUNT=y
# end of sparc Debugging

#
# Kernel Testing and Coverage
#
CONFIG_KUNIT=y
CONFIG_KUNIT_DEBUGFS=y
CONFIG_KUNIT_TEST=y
CONFIG_KUNIT_EXAMPLE_TEST=y
CONFIG_KUNIT_ALL_TESTS=y
CONFIG_KUNIT_DEFAULT_ENABLED=y
CONFIG_NOTIFIER_ERROR_INJECTION=y
CONFIG_PM_NOTIFIER_ERROR_INJECT=y
CONFIG_OF_RECONFIG_NOTIFIER_ERROR_INJECT=y
CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAILSLAB=y
CONFIG_FAIL_PAGE_ALLOC=y
CONFIG_FAULT_INJECTION_USERCOPY=y
CONFIG_FAIL_MAKE_REQUEST=y
CONFIG_FAIL_IO_TIMEOUT=y
CONFIG_FAIL_FUTEX=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAIL_MMC_REQUEST=y
CONFIG_FAIL_SUNRPC=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_CC_HAS_SANCOV_TRACE_PC=y
CONFIG_RUNTIME_TESTING_MENU=y
CONFIG_LKDTM=y
CONFIG_CPUMASK_KUNIT_TEST=y
CONFIG_TEST_LIST_SORT=y
CONFIG_TEST_MIN_HEAP=y
CONFIG_TEST_SORT=y
CONFIG_TEST_DIV64=y
CONFIG_KPROBES_SANITY_TEST=y
CONFIG_BACKTRACE_SELF_TEST=y
CONFIG_TEST_REF_TRACKER=y
CONFIG_RBTREE_TEST=y
CONFIG_REED_SOLOMON_TEST=y
CONFIG_INTERVAL_TREE_TEST=y
CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y
CONFIG_ASYNC_RAID6_TEST=y
CONFIG_TEST_HEXDUMP=y
CONFIG_STRING_SELFTEST=y
CONFIG_TEST_STRING_HELPERS=y
CONFIG_TEST_STRSCPY=y
CONFIG_TEST_KSTRTOX=y
CONFIG_TEST_PRINTF=y
CONFIG_TEST_SCANF=y
CONFIG_TEST_BITMAP=y
CONFIG_TEST_UUID=y
CONFIG_TEST_XARRAY=y
CONFIG_TEST_MAPLE_TREE=y
CONFIG_TEST_RHASHTABLE=y
CONFIG_TEST_SIPHASH=y
CONFIG_TEST_IDA=y
CONFIG_TEST_PARMAN=y
CONFIG_TEST_LKM=m
CONFIG_TEST_BITOPS=m
CONFIG_TEST_VMALLOC=m
CONFIG_TEST_USER_COPY=m
CONFIG_TEST_BPF=m
CONFIG_TEST_BLACKHOLE_DEV=m
CONFIG_FIND_BIT_BENCHMARK=y
CONFIG_TEST_FIRMWARE=y
CONFIG_TEST_SYSCTL=y
CONFIG_BITFIELD_KUNIT=y
CONFIG_HASH_KUNIT_TEST=y
CONFIG_RESOURCE_KUNIT_TEST=y
CONFIG_SYSCTL_KUNIT_TEST=y
CONFIG_LIST_KUNIT_TEST=y
CONFIG_LINEAR_RANGES_TEST=y
CONFIG_CMDLINE_KUNIT_TEST=y
CONFIG_BITS_TEST=y
CONFIG_SLUB_KUNIT_TEST=y
CONFIG_RATIONAL_KUNIT_TEST=y
CONFIG_MEMCPY_KUNIT_TEST=y
CONFIG_IS_SIGNED_TYPE_KUNIT_TEST=y
CONFIG_OVERFLOW_KUNIT_TEST=y
CONFIG_STACKINIT_KUNIT_TEST=y
CONFIG_TEST_UDELAY=y
CONFIG_TEST_STATIC_KEYS=m
CONFIG_TEST_DYNAMIC_DEBUG=y
CONFIG_TEST_KMOD=m
CONFIG_TEST_MEMCAT_P=y
CONFIG_TEST_OBJAGG=y
CONFIG_TEST_MEMINIT=y
CONFIG_TEST_FREE_PAGES=y
# end of Kernel Testing and Coverage

#
# Rust hacking
#
# end of Rust hacking

CONFIG_WARN_MISSING_DOCUMENTS=y
CONFIG_WARN_ABI_ERRORS=y
# end of Kernel hacking

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-11-29 21:37 ` Vadim Fedorenko
@ 2022-11-30 12:32   ` Jiri Pirko
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-11-30 12:32   ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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
>

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

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-11-29 21:37   ` Vadim Fedorenko
@ 2022-11-30 12:41     ` Jiri Pirko
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-11-30 12:41     ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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
>

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

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-29 21:37   ` Vadim Fedorenko
@ 2022-11-30 15:21     ` Jiri Pirko
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-11-30 15:21     ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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
>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-11-30 16:23       ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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 :)

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

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

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-29 21:37   ` Vadim Fedorenko
@ 2022-11-30 16:37     ` Jiri Pirko
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-11-30 16:37     ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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)


[...]


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

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-11-30 12:32   ` Jiri Pirko
@ 2022-12-02 11:27     ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-02 11:27     ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-12-02 11:27       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-02 11:27       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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)
>
>
>[...]


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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-02 12:39         ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?

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

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

* Re: [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
  2022-11-29 21:37   ` Vadim Fedorenko
  (?)
@ 2022-12-02 12:43   ` kernel test robot
  -1 siblings, 0 replies; 172+ messages in thread
From: kernel test robot @ 2022-12-02 12:43 UTC (permalink / raw)
  To: Vadim Fedorenko; +Cc: oe-kbuild-all

[-- Attachment #1: Type: text/plain, Size: 3294 bytes --]

Hi Vadim,

[FYI, it's a private test report for your RFC patch.]
[auto build test WARNING on linus/master]
[also build test WARNING on v6.1-rc7 next-20221202]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vadim-Fedorenko/Create-common-DPLL-clock-configuration-API/20221130-094051
patch link:    https://lore.kernel.org/r/20221129213724.10119-4-vfedorenko%40novek.ru
patch subject: [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
reproduce:
        # https://github.com/intel-lab-lkp/linux/commit/4545e6136427b48a5e9635d93ba585e432332036
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vadim-Fedorenko/Create-common-DPLL-clock-configuration-API/20221130-094051
        git checkout 4545e6136427b48a5e9635d93ba585e432332036
        make menuconfig
        # enable CONFIG_COMPILE_TEST, CONFIG_WARN_MISSING_DOCUMENTS, CONFIG_WARN_ABI_ERRORS
        make htmldocs

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> Documentation/networking/dpll.rst:52: WARNING: Malformed table.

vim +52 Documentation/networking/dpll.rst

    51	
  > 52	  ============================  =======================================
    53	  ``DEVICE_GET``                userspace to get device info
    54	    ``ID``                      attr internal dpll device index
    55	    ``NAME``                    attr dpll device name
    56	    ``MODE``                    attr selection mode
    57	    ``MODE_SUPPORTED``          attr available selection modes
    58	    ``SOURCE_PIN_IDX``          attr index of currently selected source
    59	    ``LOCK_STATUS``             attr internal frequency-lock status
    60	    ``TEMP``                    attr device temperature information
    61	    ``NETIFINDEX``              attr dpll owner Linux netdevice index
    62	  ``DEVICE_SET``                userspace to set dpll device
    63	                                configuration
    64	    ``ID``                      attr internal dpll device index
    65	    ``MODE``                    attr selection mode to configure
    66	    ``PIN_IDX``                 attr index of source pin to select as
    67	                                active source
    68	  ``PIN_SET``                   userspace to set pins configuration
    69	    ``ID``                      attr internal dpll device index
    70	    ``PIN_IDX``                 attr index of a pin to configure
    71	    ``PIN_TYPE``                attr type configuration value for
    72	                                selected pin
    73	    ``PIN_SIGNAL_TYPE``         attr signal type configuration value
    74	                                for selected pin
    75	    ``PIN_CUSTOM_FREQ``         attr signal custom frequency to be set
    76	    ``PIN_STATE``               attr pin state to be set
    77	    ``PIN_PRIO``                attr pin priority to be set
    78	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

[-- Attachment #2: config --]
[-- Type: text/plain, Size: 38906 bytes --]

#
# Automatically generated file; DO NOT EDIT.
# Linux/x86_64 6.1.0-rc7 Kernel Configuration
#
CONFIG_CC_VERSION_TEXT="gcc-11 (Debian 11.3.0-8) 11.3.0"
CONFIG_CC_IS_GCC=y
CONFIG_GCC_VERSION=110300
CONFIG_CLANG_VERSION=0
CONFIG_AS_IS_GNU=y
CONFIG_AS_VERSION=23900
CONFIG_LD_IS_BFD=y
CONFIG_LD_VERSION=23900
CONFIG_LLD_VERSION=0
CONFIG_CC_CAN_LINK=y
CONFIG_CC_CAN_LINK_STATIC=y
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
CONFIG_CC_HAS_ASM_INLINE=y
CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y
CONFIG_PAHOLE_VERSION=123
CONFIG_IRQ_WORK=y
CONFIG_BUILDTIME_TABLE_SORT=y
CONFIG_THREAD_INFO_IN_TASK=y

#
# General setup
#
CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_COMPILE_TEST=y
# CONFIG_WERROR is not set
CONFIG_LOCALVERSION=""
CONFIG_BUILD_SALT=""
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_BZIP2=y
CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_XZ=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_KERNEL_LZ4=y
CONFIG_HAVE_KERNEL_ZSTD=y
CONFIG_KERNEL_GZIP=y
# CONFIG_KERNEL_BZIP2 is not set
# CONFIG_KERNEL_LZMA is not set
# CONFIG_KERNEL_XZ is not set
# CONFIG_KERNEL_LZO is not set
# CONFIG_KERNEL_LZ4 is not set
# CONFIG_KERNEL_ZSTD is not set
CONFIG_DEFAULT_INIT=""
CONFIG_DEFAULT_HOSTNAME="(none)"
# CONFIG_SYSVIPC is not set
# CONFIG_WATCH_QUEUE is not set
# CONFIG_CROSS_MEMORY_ATTACH is not set
# CONFIG_USELIB is not set
CONFIG_HAVE_ARCH_AUDITSYSCALL=y

#
# IRQ subsystem
#
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y
CONFIG_GENERIC_IRQ_RESERVATION_MODE=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_SPARSE_IRQ=y
# end of IRQ subsystem

CONFIG_CLOCKSOURCE_WATCHDOG=y
CONFIG_ARCH_CLOCKSOURCE_INIT=y
CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y
CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y

#
# Timers subsystem
#
CONFIG_HZ_PERIODIC=y
# CONFIG_NO_HZ_IDLE is not set
# CONFIG_NO_HZ is not set
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=100
# end of Timers subsystem

CONFIG_HAVE_EBPF_JIT=y
CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y

#
# BPF subsystem
#
# CONFIG_BPF_SYSCALL is not set
# end of BPF subsystem

CONFIG_PREEMPT_NONE_BUILD=y
CONFIG_PREEMPT_NONE=y
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT is not set
# CONFIG_PREEMPT_DYNAMIC is not set

#
# CPU/Task time and stats accounting
#
CONFIG_TICK_CPU_ACCOUNTING=y
# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
# CONFIG_IRQ_TIME_ACCOUNTING is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_PSI is not set
# end of CPU/Task time and stats accounting

CONFIG_CPU_ISOLATION=y

#
# RCU Subsystem
#
CONFIG_TINY_RCU=y
# CONFIG_RCU_EXPERT is not set
CONFIG_SRCU=y
CONFIG_TINY_SRCU=y
# end of RCU Subsystem

# CONFIG_IKCONFIG is not set
# CONFIG_IKHEADERS is not set
CONFIG_LOG_BUF_SHIFT=17
CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13
CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y

#
# Scheduler features
#
# end of Scheduler features

CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y
CONFIG_CC_HAS_INT128=y
CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5"
CONFIG_GCC12_NO_ARRAY_BOUNDS=y
CONFIG_ARCH_SUPPORTS_INT128=y
# CONFIG_CGROUPS is not set
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_TIME_NS is not set
# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
# CONFIG_CHECKPOINT_RESTORE is not set
# CONFIG_SCHED_AUTOGROUP is not set
# CONFIG_SYSFS_DEPRECATED is not set
# CONFIG_RELAY is not set
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_BOOT_CONFIG is not set
# CONFIG_INITRAMFS_PRESERVE_MTIME is not set
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_LD_ORPHAN_WARN=y
CONFIG_SYSCTL=y
CONFIG_SYSCTL_EXCEPTION_TRACE=y
CONFIG_HAVE_PCSPKR_PLATFORM=y
# CONFIG_EXPERT is not set
CONFIG_MULTIUSER=y
CONFIG_SGETMASK_SYSCALL=y
CONFIG_SYSFS_SYSCALL=y
CONFIG_FHANDLE=y
CONFIG_POSIX_TIMERS=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_PCSPKR_PLATFORM=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_FUTEX_PI=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
CONFIG_IO_URING=y
CONFIG_ADVISE_SYSCALLS=y
CONFIG_MEMBARRIER=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_BASE_RELATIVE=y
CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y
CONFIG_RSEQ=y
# CONFIG_EMBEDDED is not set
CONFIG_HAVE_PERF_EVENTS=y

#
# Kernel Performance Events And Counters
#
CONFIG_PERF_EVENTS=y
# end of Kernel Performance Events And Counters

# CONFIG_PROFILING is not set
# end of General setup

CONFIG_64BIT=y
CONFIG_X86_64=y
CONFIG_X86=y
CONFIG_INSTRUCTION_DECODER=y
CONFIG_OUTPUT_FORMAT="elf64-x86-64"
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_MMU=y
CONFIG_ARCH_MMAP_RND_BITS_MIN=28
CONFIG_ARCH_MMAP_RND_BITS_MAX=32
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8
CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
CONFIG_ARCH_MAY_HAVE_PC_FDC=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_ARCH_HAS_CPU_RELAX=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_NR_GPIO=1024
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_AUDIT_ARCH=y
CONFIG_ARCH_SUPPORTS_UPROBES=y
CONFIG_FIX_EARLYCON_MEM=y
CONFIG_PGTABLE_LEVELS=4
CONFIG_CC_HAS_SANE_STACKPROTECTOR=y

#
# Processor type and features
#
# CONFIG_SMP is not set
CONFIG_X86_FEATURE_NAMES=y
CONFIG_X86_MPPARSE=y
# CONFIG_GOLDFISH is not set
# CONFIG_X86_CPU_RESCTRL is not set
# CONFIG_X86_EXTENDED_PLATFORM is not set
# CONFIG_SCHED_OMIT_FRAME_POINTER is not set
# CONFIG_HYPERVISOR_GUEST is not set
# CONFIG_MK8 is not set
# CONFIG_MPSC is not set
# CONFIG_MCORE2 is not set
# CONFIG_MATOM is not set
CONFIG_GENERIC_CPU=y
CONFIG_X86_INTERNODE_CACHE_SHIFT=6
CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_X86_TSC=y
CONFIG_X86_CMPXCHG64=y
CONFIG_X86_CMOV=y
CONFIG_X86_MINIMUM_CPU_FAMILY=64
CONFIG_X86_DEBUGCTLMSR=y
CONFIG_IA32_FEAT_CTL=y
CONFIG_X86_VMX_FEATURE_NAMES=y
CONFIG_CPU_SUP_INTEL=y
CONFIG_CPU_SUP_AMD=y
CONFIG_CPU_SUP_HYGON=y
CONFIG_CPU_SUP_CENTAUR=y
CONFIG_CPU_SUP_ZHAOXIN=y
CONFIG_HPET_TIMER=y
CONFIG_DMI=y
CONFIG_NR_CPUS_RANGE_BEGIN=1
CONFIG_NR_CPUS_RANGE_END=1
CONFIG_NR_CPUS_DEFAULT=1
CONFIG_NR_CPUS=1
CONFIG_UP_LATE_INIT=y
CONFIG_X86_LOCAL_APIC=y
CONFIG_X86_IO_APIC=y
# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set
# CONFIG_X86_MCE is not set

#
# Performance monitoring
#
# CONFIG_PERF_EVENTS_AMD_POWER is not set
# CONFIG_PERF_EVENTS_AMD_UNCORE is not set
# CONFIG_PERF_EVENTS_AMD_BRS is not set
# end of Performance monitoring

CONFIG_X86_16BIT=y
CONFIG_X86_ESPFIX64=y
CONFIG_X86_VSYSCALL_EMULATION=y
# CONFIG_X86_IOPL_IOPERM is not set
# CONFIG_MICROCODE is not set
# CONFIG_X86_MSR is not set
# CONFIG_X86_CPUID is not set
# CONFIG_X86_5LEVEL is not set
CONFIG_X86_DIRECT_GBPAGES=y
# CONFIG_AMD_MEM_ENCRYPT is not set
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_DEFAULT=y
CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
CONFIG_MTRR=y
# CONFIG_MTRR_SANITIZER is not set
CONFIG_X86_PAT=y
CONFIG_ARCH_USES_PG_UNCACHED=y
CONFIG_X86_UMIP=y
CONFIG_CC_HAS_IBT=y
# CONFIG_X86_KERNEL_IBT is not set
# CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS is not set
CONFIG_X86_INTEL_TSX_MODE_OFF=y
# CONFIG_X86_INTEL_TSX_MODE_ON is not set
# CONFIG_X86_INTEL_TSX_MODE_AUTO is not set
# CONFIG_HZ_100 is not set
CONFIG_HZ_250=y
# CONFIG_HZ_300 is not set
# CONFIG_HZ_1000 is not set
CONFIG_HZ=250
# CONFIG_KEXEC is not set
# CONFIG_CRASH_DUMP is not set
CONFIG_PHYSICAL_START=0x1000000
# CONFIG_RELOCATABLE is not set
CONFIG_PHYSICAL_ALIGN=0x200000
CONFIG_LEGACY_VSYSCALL_XONLY=y
# CONFIG_LEGACY_VSYSCALL_NONE is not set
# CONFIG_CMDLINE_BOOL is not set
CONFIG_MODIFY_LDT_SYSCALL=y
# CONFIG_STRICT_SIGALTSTACK_SIZE is not set
CONFIG_HAVE_LIVEPATCH=y
# end of Processor type and features

CONFIG_CC_HAS_SLS=y
CONFIG_CC_HAS_RETURN_THUNK=y
# CONFIG_SPECULATION_MITIGATIONS is not set
CONFIG_ARCH_HAS_ADD_PAGES=y
CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y

#
# Power management and ACPI options
#
# CONFIG_SUSPEND is not set
# CONFIG_PM is not set
CONFIG_ARCH_SUPPORTS_ACPI=y
# CONFIG_ACPI is not set

#
# CPU Frequency scaling
#
# CONFIG_CPU_FREQ is not set
# end of CPU Frequency scaling

#
# CPU Idle
#
# CONFIG_CPU_IDLE is not set
# end of CPU Idle
# end of Power management and ACPI options

#
# Bus options (PCI etc.)
#
CONFIG_ISA_DMA_API=y
# end of Bus options (PCI etc.)

#
# Binary Emulations
#
# CONFIG_IA32_EMULATION is not set
# CONFIG_X86_X32_ABI is not set
# end of Binary Emulations

CONFIG_HAVE_KVM=y
# CONFIG_VIRTUALIZATION is not set
CONFIG_AS_AVX512=y
CONFIG_AS_SHA1_NI=y
CONFIG_AS_SHA256_NI=y
CONFIG_AS_TPAUSE=y

#
# General architecture-dependent options
#
CONFIG_GENERIC_ENTRY=y
# CONFIG_JUMP_LABEL is not set
# CONFIG_STATIC_CALL_SELFTEST is not set
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
CONFIG_HAVE_IOREMAP_PROT=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_OPTPROBES=y
CONFIG_HAVE_KPROBES_ON_FTRACE=y
CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y
CONFIG_HAVE_NMI=y
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_HAVE_DMA_CONTIGUOUS=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_ARCH_HAS_FORTIFY_SOURCE=y
CONFIG_ARCH_HAS_SET_MEMORY=y
CONFIG_ARCH_HAS_SET_DIRECT_MAP=y
CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y
CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y
CONFIG_ARCH_WANTS_NO_INSTR=y
CONFIG_HAVE_ASM_MODVERSIONS=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_RSEQ=y
CONFIG_HAVE_RUST=y
CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y
CONFIG_HAVE_HW_BREAKPOINT=y
CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y
CONFIG_HAVE_USER_RETURN_NOTIFIER=y
CONFIG_HAVE_PERF_EVENTS_NMI=y
CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y
CONFIG_HAVE_PERF_REGS=y
CONFIG_HAVE_PERF_USER_STACK_DUMP=y
CONFIG_HAVE_ARCH_JUMP_LABEL=y
CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y
CONFIG_MMU_GATHER_MERGE_VMAS=y
CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
CONFIG_HAVE_CMPXCHG_LOCAL=y
CONFIG_HAVE_CMPXCHG_DOUBLE=y
CONFIG_HAVE_ARCH_SECCOMP=y
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
# CONFIG_SECCOMP is not set
CONFIG_HAVE_ARCH_STACKLEAK=y
CONFIG_HAVE_STACKPROTECTOR=y
# CONFIG_STACKPROTECTOR is not set
CONFIG_ARCH_SUPPORTS_LTO_CLANG=y
CONFIG_ARCH_SUPPORTS_LTO_CLANG_THIN=y
CONFIG_LTO_NONE=y
CONFIG_ARCH_SUPPORTS_CFI_CLANG=y
CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y
CONFIG_HAVE_CONTEXT_TRACKING_USER=y
CONFIG_HAVE_CONTEXT_TRACKING_USER_OFFSTACK=y
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
CONFIG_HAVE_MOVE_PUD=y
CONFIG_HAVE_MOVE_PMD=y
CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD=y
CONFIG_HAVE_ARCH_HUGE_VMAP=y
CONFIG_HAVE_ARCH_HUGE_VMALLOC=y
CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
CONFIG_HAVE_ARCH_SOFT_DIRTY=y
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
CONFIG_MODULES_USE_ELF_RELA=y
CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y
CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK=y
CONFIG_SOFTIRQ_ON_OWN_STACK=y
CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
CONFIG_HAVE_ARCH_MMAP_RND_BITS=y
CONFIG_HAVE_EXIT_THREAD=y
CONFIG_ARCH_MMAP_RND_BITS=28
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
CONFIG_HAVE_OBJTOOL=y
CONFIG_HAVE_JUMP_LABEL_HACK=y
CONFIG_HAVE_NOINSTR_HACK=y
CONFIG_HAVE_NOINSTR_VALIDATION=y
CONFIG_HAVE_UACCESS_VALIDATION=y
CONFIG_HAVE_STACK_VALIDATION=y
CONFIG_HAVE_RELIABLE_STACKTRACE=y
# CONFIG_COMPAT_32BIT_TIME is not set
CONFIG_HAVE_ARCH_VMAP_STACK=y
# CONFIG_VMAP_STACK is not set
CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET=y
CONFIG_RANDOMIZE_KSTACK_OFFSET=y
# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set
CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y
CONFIG_STRICT_KERNEL_RWX=y
CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y
CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y
CONFIG_ARCH_HAS_MEM_ENCRYPT=y
CONFIG_HAVE_STATIC_CALL=y
CONFIG_HAVE_STATIC_CALL_INLINE=y
CONFIG_HAVE_PREEMPT_DYNAMIC=y
CONFIG_HAVE_PREEMPT_DYNAMIC_CALL=y
CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y
CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
CONFIG_ARCH_SUPPORTS_PAGE_TABLE_CHECK=y
CONFIG_ARCH_HAS_ELFCORE_COMPAT=y
CONFIG_ARCH_HAS_PARANOID_L1D_FLUSH=y
CONFIG_DYNAMIC_SIGFRAME=y
CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y

#
# GCOV-based kernel profiling
#
CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
# end of GCOV-based kernel profiling

CONFIG_HAVE_GCC_PLUGINS=y
# CONFIG_GCC_PLUGINS is not set
# end of General architecture-dependent options

CONFIG_RT_MUTEXES=y
CONFIG_BASE_SMALL=0
# CONFIG_MODULES is not set
CONFIG_BLOCK=y
# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set
# CONFIG_BLK_DEV_BSGLIB is not set
# CONFIG_BLK_DEV_INTEGRITY is not set
# CONFIG_BLK_DEV_ZONED is not set
# CONFIG_BLK_WBT is not set
# CONFIG_BLK_SED_OPAL is not set
# CONFIG_BLK_INLINE_ENCRYPTION is not set

#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
CONFIG_EFI_PARTITION=y
# end of Partition Types

#
# IO Schedulers
#
# CONFIG_MQ_IOSCHED_DEADLINE is not set
# CONFIG_MQ_IOSCHED_KYBER is not set
# CONFIG_IOSCHED_BFQ is not set
# end of IO Schedulers

CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
CONFIG_INLINE_READ_UNLOCK=y
CONFIG_INLINE_READ_UNLOCK_IRQ=y
CONFIG_INLINE_WRITE_UNLOCK=y
CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y
CONFIG_ARCH_HAS_SYNC_CORE_BEFORE_USERMODE=y
CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y

#
# Executable file formats
#
# CONFIG_BINFMT_ELF is not set
# CONFIG_BINFMT_SCRIPT is not set
# CONFIG_BINFMT_MISC is not set
CONFIG_COREDUMP=y
# end of Executable file formats

#
# Memory Management options
#
# CONFIG_SWAP is not set

#
# SLAB allocator options
#
# CONFIG_SLAB is not set
CONFIG_SLUB=y
# CONFIG_SLAB_MERGE_DEFAULT is not set
# CONFIG_SLAB_FREELIST_RANDOM is not set
# CONFIG_SLAB_FREELIST_HARDENED is not set
# CONFIG_SLUB_STATS is not set
# end of SLAB allocator options

# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
# CONFIG_COMPAT_BRK is not set
CONFIG_SPARSEMEM=y
CONFIG_SPARSEMEM_EXTREME=y
CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
# CONFIG_SPARSEMEM_VMEMMAP is not set
CONFIG_HAVE_FAST_GUP=y
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
# CONFIG_MEMORY_HOTPLUG is not set
CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y
# CONFIG_COMPACTION is not set
# CONFIG_PAGE_REPORTING is not set
CONFIG_PHYS_ADDR_T_64BIT=y
# CONFIG_KSM is not set
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
CONFIG_ARCH_WANTS_THP_SWAP=y
# CONFIG_TRANSPARENT_HUGEPAGE is not set
CONFIG_NEED_PER_CPU_KM=y
CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
CONFIG_HAVE_SETUP_PER_CPU_AREA=y
# CONFIG_CMA is not set
CONFIG_GENERIC_EARLY_IOREMAP=y
# CONFIG_IDLE_PAGE_TRACKING is not set
CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
CONFIG_ARCH_HAS_CURRENT_STACK_POINTER=y
CONFIG_ARCH_HAS_PTE_DEVMAP=y
CONFIG_ZONE_DMA=y
CONFIG_ZONE_DMA32=y
CONFIG_VM_EVENT_COUNTERS=y
# CONFIG_PERCPU_STATS is not set

#
# GUP_TEST needs to have DEBUG_FS enabled
#
CONFIG_ARCH_HAS_PTE_SPECIAL=y
CONFIG_SECRETMEM=y
# CONFIG_ANON_VMA_NAME is not set
# CONFIG_USERFAULTFD is not set
# CONFIG_LRU_GEN is not set

#
# Data Access Monitoring
#
# CONFIG_DAMON is not set
# end of Data Access Monitoring
# end of Memory Management options

# CONFIG_NET is not set

#
# Device Drivers
#
CONFIG_HAVE_EISA=y
# CONFIG_EISA is not set
CONFIG_HAVE_PCI=y
# CONFIG_PCI is not set
# CONFIG_PCCARD is not set

#
# Generic Driver Options
#
# CONFIG_UEVENT_HELPER is not set
# CONFIG_DEVTMPFS is not set
# CONFIG_STANDALONE is not set
# CONFIG_PREVENT_FIRMWARE_BUILD is not set

#
# Firmware loader
#
CONFIG_FW_LOADER=y
CONFIG_EXTRA_FIRMWARE=""
# CONFIG_FW_LOADER_USER_HELPER is not set
# CONFIG_FW_LOADER_COMPRESS is not set
# CONFIG_FW_UPLOAD is not set
# end of Firmware loader

CONFIG_ALLOW_DEV_COREDUMP=y
CONFIG_GENERIC_CPU_AUTOPROBE=y
CONFIG_GENERIC_CPU_VULNERABILITIES=y
# end of Generic Driver Options

#
# Bus devices
#
# CONFIG_ARM_INTEGRATOR_LM is not set
# CONFIG_BT1_APB is not set
# CONFIG_BT1_AXI is not set
# CONFIG_HISILICON_LPC is not set
# CONFIG_INTEL_IXP4XX_EB is not set
# CONFIG_QCOM_EBI2 is not set
# CONFIG_MHI_BUS is not set
# CONFIG_MHI_BUS_EP is not set
# end of Bus devices

#
# Firmware Drivers
#

#
# ARM System Control and Management Interface Protocol
#
# CONFIG_ARM_SCMI_PROTOCOL is not set
# end of ARM System Control and Management Interface Protocol

# CONFIG_EDD is not set
CONFIG_FIRMWARE_MEMMAP=y
# CONFIG_DMIID is not set
# CONFIG_DMI_SYSFS is not set
CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y
# CONFIG_FW_CFG_SYSFS is not set
# CONFIG_SYSFB_SIMPLEFB is not set
# CONFIG_BCM47XX_NVRAM is not set
# CONFIG_GOOGLE_FIRMWARE is not set

#
# Tegra firmware driver
#
# end of Tegra firmware driver
# end of Firmware Drivers

# CONFIG_GNSS is not set
# CONFIG_MTD is not set
# CONFIG_OF is not set
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
# CONFIG_PARPORT is not set
# CONFIG_BLK_DEV is not set

#
# NVME Support
#
# CONFIG_NVME_FC is not set
# end of NVME Support

#
# Misc devices
#
# CONFIG_DUMMY_IRQ is not set
# CONFIG_ATMEL_SSC is not set
# CONFIG_ENCLOSURE_SERVICES is not set
# CONFIG_QCOM_COINCELL is not set
# CONFIG_SRAM is not set
# CONFIG_XILINX_SDFEC is not set
# CONFIG_C2PORT is not set

#
# EEPROM support
#
# CONFIG_EEPROM_93CX6 is not set
# end of EEPROM support

#
# Texas Instruments shared transport line discipline
#
# end of Texas Instruments shared transport line discipline

#
# Altera FPGA firmware download module (requires I2C)
#
# CONFIG_ECHO is not set
# CONFIG_PVPANIC is not set
# end of Misc devices

#
# SCSI device support
#
CONFIG_SCSI_MOD=y
# CONFIG_RAID_ATTRS is not set
# CONFIG_SCSI is not set
# end of SCSI device support

# CONFIG_ATA is not set
# CONFIG_MD is not set
# CONFIG_TARGET_CORE is not set

#
# IEEE 1394 (FireWire) support
#
# CONFIG_FIREWIRE is not set
# end of IEEE 1394 (FireWire) support

# CONFIG_MACINTOSH_DRIVERS is not set

#
# Input device support
#
CONFIG_INPUT=y
# CONFIG_INPUT_FF_MEMLESS is not set
# CONFIG_INPUT_SPARSEKMAP is not set
# CONFIG_INPUT_MATRIXKMAP is not set

#
# Userland interfaces
#
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_EVDEV is not set
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
# CONFIG_RMI4_CORE is not set

#
# Hardware I/O ports
#
# CONFIG_SERIO is not set
CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
# CONFIG_GAMEPORT is not set
# end of Hardware I/O ports
# end of Input device support

#
# Character devices
#
CONFIG_TTY=y
CONFIG_VT=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
# CONFIG_VT_HW_CONSOLE_BINDING is not set
CONFIG_UNIX98_PTYS=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_LDISC_AUTOLOAD is not set

#
# Serial drivers
#
# CONFIG_SERIAL_8250 is not set

#
# Non-8250 serial port support
#
# CONFIG_SERIAL_AMBA_PL010 is not set
# CONFIG_SERIAL_MESON is not set
# CONFIG_SERIAL_CLPS711X is not set
# CONFIG_SERIAL_SAMSUNG is not set
# CONFIG_SERIAL_TEGRA is not set
# CONFIG_SERIAL_IMX is not set
# CONFIG_SERIAL_UARTLITE is not set
# CONFIG_SERIAL_SH_SCI is not set
# CONFIG_SERIAL_MSM is not set
# CONFIG_SERIAL_VT8500 is not set
# CONFIG_SERIAL_OMAP is not set
# CONFIG_SERIAL_LANTIQ is not set
# CONFIG_SERIAL_SCCNXP is not set
# CONFIG_SERIAL_TIMBERDALE is not set
# CONFIG_SERIAL_BCM63XX is not set
# CONFIG_SERIAL_ALTERA_JTAGUART is not set
# CONFIG_SERIAL_ALTERA_UART is not set
# CONFIG_SERIAL_MXS_AUART is not set
# CONFIG_SERIAL_MPS2_UART is not set
# CONFIG_SERIAL_ARC is not set
# CONFIG_SERIAL_FSL_LPUART is not set
# CONFIG_SERIAL_FSL_LINFLEXUART is not set
# CONFIG_SERIAL_ST_ASC is not set
# CONFIG_SERIAL_STM32 is not set
# CONFIG_SERIAL_OWL is not set
# CONFIG_SERIAL_RDA is not set
# CONFIG_SERIAL_LITEUART is not set
# CONFIG_SERIAL_SUNPLUS is not set
# end of Serial drivers

# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_NULL_TTY is not set
# CONFIG_SERIAL_DEV_BUS is not set
# CONFIG_VIRTIO_CONSOLE is not set
# CONFIG_IPMI_HANDLER is not set
# CONFIG_ASPEED_KCS_IPMI_BMC is not set
# CONFIG_NPCM7XX_KCS_IPMI_BMC is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_MWAVE is not set
# CONFIG_DEVMEM is not set
# CONFIG_NVRAM is not set
# CONFIG_HANGCHECK_TIMER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_TELCLOCK is not set
# CONFIG_RANDOM_TRUST_CPU is not set
# CONFIG_RANDOM_TRUST_BOOTLOADER is not set
# end of Character devices

#
# I2C support
#
# CONFIG_I2C is not set
# end of I2C support

# CONFIG_I3C is not set
# CONFIG_SPI is not set
# CONFIG_SPMI is not set
# CONFIG_HSI is not set
# CONFIG_PPS is not set

#
# PTP clock support
#
CONFIG_PTP_1588_CLOCK_OPTIONAL=y

#
# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
#
# end of PTP clock support

# CONFIG_PINCTRL is not set
# CONFIG_GPIOLIB is not set
# CONFIG_W1 is not set
# CONFIG_POWER_RESET is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
# CONFIG_THERMAL is not set
# CONFIG_WATCHDOG is not set
CONFIG_SSB_POSSIBLE=y
# CONFIG_SSB is not set
CONFIG_BCMA_POSSIBLE=y
# CONFIG_BCMA is not set

#
# Multifunction device drivers
#
# CONFIG_MFD_SUN4I_GPADC is not set
# CONFIG_MFD_AT91_USART is not set
# CONFIG_MFD_MADERA is not set
# CONFIG_MFD_EXYNOS_LPASS is not set
# CONFIG_MFD_MXS_LRADC is not set
# CONFIG_MFD_MX25_TSADC is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_KEMPLD is not set
# CONFIG_MFD_MT6397 is not set
# CONFIG_MFD_PM8XXX is not set
# CONFIG_MFD_SM501 is not set
# CONFIG_ABX500_CORE is not set
# CONFIG_MFD_SUN6I_PRCM is not set
# CONFIG_MFD_SYSCON is not set
# CONFIG_MFD_TI_AM335X_TSCADC is not set
# CONFIG_MFD_TQMX86 is not set
# CONFIG_MFD_STM32_LPTIMER is not set
# CONFIG_MFD_STM32_TIMERS is not set
# end of Multifunction device drivers

# CONFIG_REGULATOR is not set
# CONFIG_RC_CORE is not set

#
# CEC support
#
# CONFIG_MEDIA_CEC_SUPPORT is not set
# end of CEC support

# CONFIG_MEDIA_SUPPORT is not set

#
# Graphics support
#
# CONFIG_IMX_IPUV3_CORE is not set
# CONFIG_DRM is not set

#
# ARM devices
#
# end of ARM devices

#
# Frame buffer Devices
#
# CONFIG_FB is not set
# CONFIG_MMP_DISP is not set
# end of Frame buffer Devices

#
# Backlight & LCD device support
#
# CONFIG_LCD_CLASS_DEVICE is not set
# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
# end of Backlight & LCD device support

#
# Console display driver support
#
CONFIG_VGA_CONSOLE=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_DUMMY_CONSOLE_COLUMNS=80
CONFIG_DUMMY_CONSOLE_ROWS=25
# end of Console display driver support
# end of Graphics support

# CONFIG_SOUND is not set

#
# HID support
#
# CONFIG_HID is not set
# end of HID support

CONFIG_USB_OHCI_LITTLE_ENDIAN=y
# CONFIG_USB_SUPPORT is not set
# CONFIG_MMC is not set
# CONFIG_MEMSTICK is not set
# CONFIG_NEW_LEDS is not set
# CONFIG_ACCESSIBILITY is not set
CONFIG_EDAC_ATOMIC_SCRUB=y
CONFIG_EDAC_SUPPORT=y
CONFIG_RTC_LIB=y
CONFIG_RTC_MC146818_LIB=y
# CONFIG_RTC_CLASS is not set
# CONFIG_DMADEVICES is not set

#
# DMABUF options
#
# CONFIG_SYNC_FILE is not set
# CONFIG_DMABUF_HEAPS is not set
# end of DMABUF options

# CONFIG_AUXDISPLAY is not set
# CONFIG_UIO is not set
# CONFIG_VFIO is not set
# CONFIG_VIRT_DRIVERS is not set
# CONFIG_VIRTIO_MENU is not set
# CONFIG_VHOST_MENU is not set

#
# Microsoft Hyper-V guest support
#
# end of Microsoft Hyper-V guest support

# CONFIG_GREYBUS is not set
# CONFIG_COMEDI is not set
# CONFIG_STAGING is not set
# CONFIG_CHROME_PLATFORMS is not set
# CONFIG_MELLANOX_PLATFORM is not set
# CONFIG_OLPC_XO175 is not set
# CONFIG_SURFACE_PLATFORMS is not set
# CONFIG_X86_PLATFORM_DEVICES is not set
# CONFIG_COMMON_CLK is not set
# CONFIG_HWSPINLOCK is not set

#
# Clock Source drivers
#
CONFIG_CLKEVT_I8253=y
CONFIG_I8253_LOCK=y
CONFIG_CLKBLD_I8253=y
# CONFIG_BCM2835_TIMER is not set
# CONFIG_BCM_KONA_TIMER is not set
# CONFIG_DAVINCI_TIMER is not set
# CONFIG_DIGICOLOR_TIMER is not set
# CONFIG_OMAP_DM_TIMER is not set
# CONFIG_DW_APB_TIMER is not set
# CONFIG_FTTMR010_TIMER is not set
# CONFIG_IXP4XX_TIMER is not set
# CONFIG_MESON6_TIMER is not set
# CONFIG_OWL_TIMER is not set
# CONFIG_RDA_TIMER is not set
# CONFIG_SUN4I_TIMER is not set
# CONFIG_TEGRA_TIMER is not set
# CONFIG_VT8500_TIMER is not set
# CONFIG_NPCM7XX_TIMER is not set
# CONFIG_ASM9260_TIMER is not set
# CONFIG_CLKSRC_DBX500_PRCMU is not set
# CONFIG_CLPS711X_TIMER is not set
# CONFIG_MXS_TIMER is not set
# CONFIG_NSPIRE_TIMER is not set
# CONFIG_INTEGRATOR_AP_TIMER is not set
# CONFIG_CLKSRC_PISTACHIO is not set
# CONFIG_CLKSRC_STM32_LP is not set
# CONFIG_ARMV7M_SYSTICK is not set
# CONFIG_ATMEL_PIT is not set
# CONFIG_ATMEL_ST is not set
# CONFIG_CLKSRC_SAMSUNG_PWM is not set
# CONFIG_FSL_FTM_TIMER is not set
# CONFIG_OXNAS_RPS_TIMER is not set
# CONFIG_MTK_TIMER is not set
# CONFIG_SH_TIMER_CMT is not set
# CONFIG_SH_TIMER_MTU2 is not set
# CONFIG_RENESAS_OSTM is not set
# CONFIG_SH_TIMER_TMU is not set
# CONFIG_EM_TIMER_STI is not set
# CONFIG_CLKSRC_PXA is not set
# CONFIG_TIMER_IMX_SYS_CTR is not set
# CONFIG_CLKSRC_ST_LPC is not set
# CONFIG_GXP_TIMER is not set
# CONFIG_MSC313E_TIMER is not set
# CONFIG_MICROCHIP_PIT64B is not set
# end of Clock Source drivers

# CONFIG_MAILBOX is not set
# CONFIG_IOMMU_SUPPORT is not set

#
# Remoteproc drivers
#
# CONFIG_REMOTEPROC is not set
# end of Remoteproc drivers

#
# Rpmsg drivers
#
# CONFIG_RPMSG_VIRTIO is not set
# end of Rpmsg drivers

#
# SOC (System On Chip) specific Drivers
#

#
# Amlogic SoC drivers
#
# CONFIG_MESON_CANVAS is not set
# CONFIG_MESON_CLK_MEASURE is not set
# CONFIG_MESON_GX_SOCINFO is not set
# CONFIG_MESON_MX_SOCINFO is not set
# end of Amlogic SoC drivers

#
# Apple SoC drivers
#
# CONFIG_APPLE_SART is not set
# end of Apple SoC drivers

#
# ASPEED SoC drivers
#
# CONFIG_ASPEED_LPC_CTRL is not set
# CONFIG_ASPEED_LPC_SNOOP is not set
# CONFIG_ASPEED_UART_ROUTING is not set
# CONFIG_ASPEED_P2A_CTRL is not set
# CONFIG_ASPEED_SOCINFO is not set
# end of ASPEED SoC drivers

# CONFIG_AT91_SOC_ID is not set
# CONFIG_AT91_SOC_SFR is not set

#
# Broadcom SoC drivers
#
# CONFIG_SOC_BCM63XX is not set
# CONFIG_SOC_BRCMSTB is not set
# end of Broadcom SoC drivers

#
# NXP/Freescale QorIQ SoC drivers
#
# end of NXP/Freescale QorIQ SoC drivers

#
# fujitsu SoC drivers
#
# end of fujitsu SoC drivers

#
# i.MX SoC drivers
#
# CONFIG_SOC_IMX8M is not set
# CONFIG_SOC_IMX9 is not set
# end of i.MX SoC drivers

#
# IXP4xx SoC drivers
#
# CONFIG_IXP4XX_QMGR is not set
# CONFIG_IXP4XX_NPE is not set
# end of IXP4xx SoC drivers

#
# Enable LiteX SoC Builder specific drivers
#
# CONFIG_LITEX_SOC_CONTROLLER is not set
# end of Enable LiteX SoC Builder specific drivers

#
# MediaTek SoC drivers
#
# CONFIG_MTK_CMDQ is not set
# CONFIG_MTK_DEVAPC is not set
# CONFIG_MTK_INFRACFG is not set
# CONFIG_MTK_MMSYS is not set
# end of MediaTek SoC drivers

#
# Qualcomm SoC drivers
#
# CONFIG_QCOM_GENI_SE is not set
# CONFIG_QCOM_GSBI is not set
# CONFIG_QCOM_LLCC is not set
# CONFIG_QCOM_RPMH is not set
# CONFIG_QCOM_SPM is not set
# CONFIG_QCOM_ICC_BWMON is not set
# end of Qualcomm SoC drivers

# CONFIG_SOC_RENESAS is not set
# CONFIG_ROCKCHIP_GRF is not set
# CONFIG_SOC_SAMSUNG is not set
# CONFIG_SOC_TI is not set
# CONFIG_UX500_SOC_ID is not set

#
# Xilinx SoC drivers
#
# end of Xilinx SoC drivers
# end of SOC (System On Chip) specific Drivers

# CONFIG_PM_DEVFREQ is not set
# CONFIG_EXTCON is not set
# CONFIG_MEMORY is not set
# CONFIG_IIO is not set
# CONFIG_PWM is not set

#
# IRQ chip support
#
# CONFIG_AL_FIC is not set
# CONFIG_RENESAS_INTC_IRQPIN is not set
# CONFIG_RENESAS_IRQC is not set
# CONFIG_RENESAS_RZA1_IRQC is not set
# CONFIG_RENESAS_RZG2L_IRQC is not set
# CONFIG_SL28CPLD_INTC is not set
# CONFIG_TS4800_IRQ is not set
# CONFIG_INGENIC_TCU_IRQ is not set
# CONFIG_IRQ_UNIPHIER_AIDET is not set
# CONFIG_MESON_IRQ_GPIO is not set
# CONFIG_IMX_IRQSTEER is not set
# CONFIG_IMX_INTMUX is not set
# CONFIG_EXYNOS_IRQ_COMBINER is not set
# CONFIG_MST_IRQ is not set
# CONFIG_MCHP_EIC is not set
# CONFIG_SUNPLUS_SP7021_INTC is not set
# end of IRQ chip support

# CONFIG_IPACK_BUS is not set
# CONFIG_RESET_CONTROLLER is not set

#
# PHY Subsystem
#
# CONFIG_GENERIC_PHY is not set
# CONFIG_PHY_PISTACHIO_USB is not set
# CONFIG_PHY_CAN_TRANSCEIVER is not set

#
# PHY drivers for Broadcom platforms
#
# CONFIG_PHY_BCM63XX_USBH is not set
# CONFIG_BCM_KONA_USB2_PHY is not set
# end of PHY drivers for Broadcom platforms

# CONFIG_PHY_HI6220_USB is not set
# CONFIG_PHY_HI3660_USB is not set
# CONFIG_PHY_HI3670_USB is not set
# CONFIG_PHY_HI3670_PCIE is not set
# CONFIG_PHY_HISTB_COMBPHY is not set
# CONFIG_PHY_HISI_INNO_USB2 is not set
# CONFIG_PHY_PXA_28NM_HSIC is not set
# CONFIG_PHY_PXA_28NM_USB2 is not set
# CONFIG_PHY_PXA_USB is not set
# CONFIG_PHY_MMP3_USB is not set
# CONFIG_PHY_MMP3_HSIC is not set
# CONFIG_PHY_MT7621_PCI is not set
# CONFIG_PHY_RALINK_USB is not set
# CONFIG_PHY_RCAR_GEN3_USB3 is not set
# CONFIG_PHY_ROCKCHIP_DPHY_RX0 is not set
# CONFIG_PHY_ROCKCHIP_PCIE is not set
# CONFIG_PHY_ROCKCHIP_SNPS_PCIE3 is not set
# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set
# CONFIG_PHY_SAMSUNG_USB2 is not set
# CONFIG_PHY_ST_SPEAR1310_MIPHY is not set
# CONFIG_PHY_ST_SPEAR1340_MIPHY is not set
# CONFIG_PHY_TEGRA194_P2U is not set
# CONFIG_PHY_DA8XX_USB is not set
# CONFIG_OMAP_CONTROL_PHY is not set
# CONFIG_TI_PIPE3 is not set
# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set
# CONFIG_PHY_INTEL_KEEMBAY_USB is not set
# CONFIG_PHY_INTEL_LGM_EMMC is not set
# CONFIG_PHY_XILINX_ZYNQMP is not set
# end of PHY Subsystem

# CONFIG_POWERCAP is not set
# CONFIG_MCB is not set

#
# Performance monitor support
#
# CONFIG_ARM_CCN is not set
# CONFIG_ARM_CMN is not set
# CONFIG_FSL_IMX8_DDR_PMU is not set
# CONFIG_XGENE_PMU is not set
# CONFIG_ARM_DMC620_PMU is not set
# CONFIG_MARVELL_CN10K_TAD_PMU is not set
# CONFIG_ALIBABA_UNCORE_DRW_PMU is not set
# CONFIG_MARVELL_CN10K_DDR_PMU is not set
# end of Performance monitor support

# CONFIG_RAS is not set

#
# Android
#
# CONFIG_ANDROID_BINDER_IPC is not set
# end of Android

# CONFIG_DAX is not set
# CONFIG_NVMEM is not set

#
# HW tracing support
#
# CONFIG_STM is not set
# CONFIG_INTEL_TH is not set
# end of HW tracing support

# CONFIG_FPGA is not set
# CONFIG_TEE is not set
# CONFIG_SIOX is not set
# CONFIG_SLIMBUS is not set
# CONFIG_INTERCONNECT is not set
# CONFIG_COUNTER is not set
# CONFIG_PECI is not set
# CONFIG_HTE is not set
# end of Device Drivers

#
# File systems
#
CONFIG_DCACHE_WORD_ACCESS=y
# CONFIG_VALIDATE_FS_PARSER is not set
# CONFIG_EXT2_FS is not set
# CONFIG_EXT3_FS is not set
# CONFIG_EXT4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_BTRFS_FS is not set
# CONFIG_NILFS2_FS is not set
# CONFIG_F2FS_FS is not set
CONFIG_EXPORTFS=y
# CONFIG_EXPORTFS_BLOCK_OPS is not set
CONFIG_FILE_LOCKING=y
# CONFIG_FS_ENCRYPTION is not set
# CONFIG_FS_VERITY is not set
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY_USER is not set
# CONFIG_FANOTIFY is not set
# CONFIG_QUOTA is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_FUSE_FS is not set
# CONFIG_OVERLAY_FS is not set

#
# Caches
#
# CONFIG_FSCACHE is not set
# end of Caches

#
# CD-ROM/DVD Filesystems
#
# CONFIG_ISO9660_FS is not set
# CONFIG_UDF_FS is not set
# end of CD-ROM/DVD Filesystems

#
# DOS/FAT/EXFAT/NT Filesystems
#
# CONFIG_MSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_EXFAT_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_NTFS3_FS is not set
# end of DOS/FAT/EXFAT/NT Filesystems

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
# CONFIG_PROC_KCORE is not set
CONFIG_PROC_SYSCTL=y
CONFIG_PROC_PAGE_MONITOR=y
# CONFIG_PROC_CHILDREN is not set
CONFIG_PROC_PID_ARCH_STATUS=y
CONFIG_KERNFS=y
CONFIG_SYSFS=y
# CONFIG_TMPFS is not set
# CONFIG_HUGETLBFS is not set
CONFIG_ARCH_WANT_HUGETLB_PAGE_OPTIMIZE_VMEMMAP=y
CONFIG_ARCH_HAS_GIGANTIC_PAGE=y
# CONFIG_CONFIGFS_FS is not set
# end of Pseudo filesystems

# CONFIG_MISC_FILESYSTEMS is not set
# CONFIG_NLS is not set
# CONFIG_UNICODE is not set
CONFIG_IO_WQ=y
# end of File systems

#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY_DMESG_RESTRICT is not set
# CONFIG_SECURITY is not set
# CONFIG_SECURITYFS is not set
CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y
# CONFIG_HARDENED_USERCOPY is not set
# CONFIG_FORTIFY_SOURCE is not set
# CONFIG_STATIC_USERMODEHELPER is not set
CONFIG_DEFAULT_SECURITY_DAC=y
CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,bpf"

#
# Kernel hardening options
#

#
# Memory initialization
#
CONFIG_INIT_STACK_NONE=y
# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set
CONFIG_CC_HAS_ZERO_CALL_USED_REGS=y
# CONFIG_ZERO_CALL_USED_REGS is not set
# end of Memory initialization

CONFIG_RANDSTRUCT_NONE=y
# end of Kernel hardening options
# end of Security options

# CONFIG_CRYPTO is not set

#
# Library routines
#
# CONFIG_PACKING is not set
CONFIG_BITREVERSE=y
CONFIG_GENERIC_STRNCPY_FROM_USER=y
CONFIG_GENERIC_STRNLEN_USER=y
# CONFIG_CORDIC is not set
# CONFIG_PRIME_NUMBERS is not set
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_IOMAP=y
CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
CONFIG_ARCH_HAS_FAST_MULTIPLIER=y
CONFIG_ARCH_USE_SYM_ANNOTATIONS=y

#
# Crypto library routines
#
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
# CONFIG_CRYPTO_LIB_CHACHA is not set
# CONFIG_CRYPTO_LIB_CURVE25519 is not set
CONFIG_CRYPTO_LIB_POLY1305_RSIZE=11
# CONFIG_CRYPTO_LIB_POLY1305 is not set
# end of Crypto library routines

# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
# CONFIG_CRC_T10DIF is not set
# CONFIG_CRC64_ROCKSOFT is not set
# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
# CONFIG_CRC32_SELFTEST is not set
CONFIG_CRC32_SLICEBY8=y
# CONFIG_CRC32_SLICEBY4 is not set
# CONFIG_CRC32_SARWATE is not set
# CONFIG_CRC32_BIT is not set
# CONFIG_CRC64 is not set
# CONFIG_CRC4 is not set
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
# CONFIG_CRC8 is not set
# CONFIG_RANDOM32_SELFTEST is not set
# CONFIG_XZ_DEC is not set
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT_MAP=y
CONFIG_HAS_DMA=y
CONFIG_NEED_SG_DMA_LENGTH=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
CONFIG_SWIOTLB=y
# CONFIG_DMA_API_DEBUG is not set
# CONFIG_IRQ_POLL is not set
CONFIG_HAVE_GENERIC_VDSO=y
CONFIG_GENERIC_GETTIMEOFDAY=y
CONFIG_GENERIC_VDSO_TIME_NS=y
CONFIG_ARCH_HAS_PMEM_API=y
CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE=y
CONFIG_ARCH_HAS_COPY_MC=y
CONFIG_ARCH_STACKWALK=y
CONFIG_STACKDEPOT=y
CONFIG_SBITMAP=y
# CONFIG_PARMAN is not set
# CONFIG_OBJAGG is not set
# end of Library routines

#
# Kernel hacking
#

#
# printk and dmesg options
#
# CONFIG_PRINTK_TIME is not set
# CONFIG_PRINTK_CALLER is not set
# CONFIG_STACKTRACE_BUILD_ID is not set
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
CONFIG_CONSOLE_LOGLEVEL_QUIET=4
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
# CONFIG_DYNAMIC_DEBUG is not set
# CONFIG_DYNAMIC_DEBUG_CORE is not set
# CONFIG_SYMBOLIC_ERRNAME is not set
CONFIG_DEBUG_BUGVERBOSE=y
# end of printk and dmesg options

# CONFIG_DEBUG_KERNEL is not set

#
# Compile-time checks and compiler options
#
CONFIG_AS_HAS_NON_CONST_LEB128=y
CONFIG_FRAME_WARN=2048
# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_HEADERS_INSTALL is not set
CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
CONFIG_OBJTOOL=y
# end of Compile-time checks and compiler options

#
# Generic Kernel Debugging Instruments
#
# CONFIG_MAGIC_SYSRQ is not set
# CONFIG_DEBUG_FS is not set
CONFIG_HAVE_ARCH_KGDB=y
CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y
# CONFIG_UBSAN is not set
CONFIG_HAVE_ARCH_KCSAN=y
CONFIG_HAVE_KCSAN_COMPILER=y
# end of Generic Kernel Debugging Instruments

#
# Networking Debugging
#
# end of Networking Debugging

#
# Memory Debugging
#
# CONFIG_PAGE_EXTENSION is not set
CONFIG_SLUB_DEBUG=y
# CONFIG_SLUB_DEBUG_ON is not set
# CONFIG_PAGE_TABLE_CHECK is not set
# CONFIG_PAGE_POISONING is not set
# CONFIG_DEBUG_RODATA_TEST is not set
CONFIG_ARCH_HAS_DEBUG_WX=y
# CONFIG_DEBUG_WX is not set
CONFIG_GENERIC_PTDUMP=y
CONFIG_HAVE_DEBUG_KMEMLEAK=y
CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y
# CONFIG_DEBUG_VM_PGTABLE is not set
CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_ARCH_SUPPORTS_KMAP_LOCAL_FORCE_MAP=y
CONFIG_HAVE_ARCH_KASAN=y
CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
CONFIG_CC_HAS_KASAN_GENERIC=y
CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y
# CONFIG_KASAN is not set
CONFIG_HAVE_ARCH_KFENCE=y
# CONFIG_KFENCE is not set
CONFIG_HAVE_ARCH_KMSAN=y
# end of Memory Debugging

#
# Debug Oops, Lockups and Hangs
#
# CONFIG_PANIC_ON_OOPS is not set
CONFIG_PANIC_ON_OOPS_VALUE=0
CONFIG_PANIC_TIMEOUT=0
CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y
# end of Debug Oops, Lockups and Hangs

#
# Scheduler Debugging
#
# end of Scheduler Debugging

# CONFIG_DEBUG_TIMEKEEPING is not set

#
# Lock Debugging (spinlocks, mutexes, etc...)
#
CONFIG_LOCK_DEBUGGING_SUPPORT=y
# CONFIG_WW_MUTEX_SELFTEST is not set
# end of Lock Debugging (spinlocks, mutexes, etc...)

# CONFIG_DEBUG_IRQFLAGS is not set
CONFIG_STACKTRACE=y
# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set

#
# Debug kernel data structures
#
# CONFIG_BUG_ON_DATA_CORRUPTION is not set
# end of Debug kernel data structures

#
# RCU Debugging
#
# end of RCU Debugging

CONFIG_USER_STACKTRACE_SUPPORT=y
CONFIG_HAVE_RETHOOK=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y
CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
CONFIG_HAVE_DYNAMIC_FTRACE_NO_PATCHABLE=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
CONFIG_HAVE_FENTRY=y
CONFIG_HAVE_OBJTOOL_MCOUNT=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
CONFIG_TRACING_SUPPORT=y
# CONFIG_FTRACE is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_SAMPLE_FTRACE_DIRECT=y
CONFIG_HAVE_SAMPLE_FTRACE_DIRECT_MULTI=y
CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y

#
# x86 Debugging
#
# CONFIG_X86_VERBOSE_BOOTUP is not set
CONFIG_EARLY_PRINTK=y
CONFIG_HAVE_MMIOTRACE_SUPPORT=y
CONFIG_IO_DELAY_0X80=y
# CONFIG_IO_DELAY_0XED is not set
# CONFIG_IO_DELAY_UDELAY is not set
# CONFIG_IO_DELAY_NONE is not set
CONFIG_UNWINDER_ORC=y
# CONFIG_UNWINDER_FRAME_POINTER is not set
# end of x86 Debugging

#
# Kernel Testing and Coverage
#
# CONFIG_KUNIT is not set
CONFIG_ARCH_HAS_KCOV=y
CONFIG_CC_HAS_SANCOV_TRACE_PC=y
# CONFIG_KCOV is not set
# CONFIG_RUNTIME_TESTING_MENU is not set
CONFIG_ARCH_USE_MEMTEST=y
# CONFIG_MEMTEST is not set
# end of Kernel Testing and Coverage

#
# Rust hacking
#
# end of Rust hacking

CONFIG_WARN_MISSING_DOCUMENTS=y
CONFIG_WARN_ABI_ERRORS=y
# end of Kernel hacking

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-12-02 12:48         ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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
>>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-12-02 14:39           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
>>>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-02 14:54           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-02 16:12       ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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
>>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-02 16:15             ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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
>>>>>

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

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-12-02 16:20             ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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] 172+ 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
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-05 10:32                 ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-06  0:19                   ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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).

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-06  8:50                     ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?


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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-06 17:27                       ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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.

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

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

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
  2022-12-02 14:39           ` Kubalewski, Arkadiusz
@ 2022-12-07  2:33             ` Jakub Kicinski
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-12-07  2:33             ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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.


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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-07  2:47         ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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 :|

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-07 13:10                         ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-12-07 13:19               ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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.



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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-07 14:09           ` netdev.dump
  0 siblings, 0 replies; 172+ 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.


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

^ permalink raw reply	[flat|nested] 172+ 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:51           ` Jiri Pirko
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-07 14:51           ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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.


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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-07 16:59                           ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-07 23:21             ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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.

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

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-02 16:12       ` Jiri Pirko
@ 2022-12-08  0:27         ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-08  0:27         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
>>>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-12-08  0:35               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
>>>>>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-08  8:14                             ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?


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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 4/4] ptp_ocp: implement DPLL ops
@ 2022-12-08  8:19                 ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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
>>>>>>>

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

^ permalink raw reply	[flat|nested] 172+ 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; 172+ 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] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-08 11:28               ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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.

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-08 11:58           ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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
>>>>>

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

^ permalink raw reply	[flat|nested] 172+ 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; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-08 12:02               ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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.

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-08 16:19                               ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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,

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-08 16:33                                 ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-08 17:05                                   ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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 18:08               ` Maciek Machnikowski
  -1 siblings, 0 replies; 172+ 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

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

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-08 18:08               ` Maciek Machnikowski
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
       [not found]           ` <20221207091946.3115742f@kernel.org>
@ 2022-12-08 18:23               ` Kubalewski, Arkadiusz
  2022-12-08 18:23               ` Kubalewski, Arkadiusz
  1 sibling, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-08 18:23               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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.

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-08 23:05             ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
>>>>>>

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

^ permalink raw reply	[flat|nested] 172+ 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; 172+ 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] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-09  0:39                 ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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.

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

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-07 23:21             ` Jakub Kicinski
@ 2022-12-09  0:46               ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-09  0:46               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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.

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-09  0:56                   ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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.

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-09  9:29                                     ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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 :/

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

^ permalink raw reply	[flat|nested] 172+ 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; 172+ 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] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-09 10:01               ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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.


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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-09 11:07                 ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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


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

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-09 14:09                   ` Maciek Machnikowski
  0 siblings, 0 replies; 172+ 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] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-09 16:19                                       ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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.

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-09 16:31                     ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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?

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-09 17:11                       ` Maciek Machnikowski
  0 siblings, 0 replies; 172+ 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.


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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-12 13:36                                         ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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.


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

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-12-09 16:31                     ` Jakub Kicinski
@ 2022-12-12 13:58                       ` Jiri Pirko
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2022-12-12 13:58                       ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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?

[...]

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-13 18:08                                           ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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.


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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-14  7:32                                             ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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.
>

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

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

* Re: [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
  2022-11-29 21:37   ` Vadim Fedorenko
@ 2022-12-19  9:13     ` Paolo Abeni
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
@ 2022-12-19  9:13     ` Paolo Abeni
  0 siblings, 0 replies; 172+ 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


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

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

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
  2022-11-30 15:21     ` Jiri Pirko
@ 2022-12-23 16:45       ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2022-12-23 16:45       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions
@ 2023-01-02 12:28         ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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
>>>

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-09 14:43                         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-09 16:30                           ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-10 10:54                             ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-10 14:28                               ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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

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

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2023-01-09 14:43                         ` Kubalewski, Arkadiusz
@ 2023-01-10 20:05                           ` Jakub Kicinski
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-10 20:05                           ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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.

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11  8:19                             ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 14:16                               ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 14:17                                 ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 14:40                                   ` Maciek Machnikowski
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 15:04                                 ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 15:30                                     ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 15:30                                   ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 15:54                                       ` Maciek Machnikowski
  0 siblings, 0 replies; 172+ 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.

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 16:14                                     ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-11 16:27                                         ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-12 12:15                                       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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

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

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

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
  2022-11-29 21:37 ` Vadim Fedorenko
@ 2023-01-12 12:23   ` Kubalewski, Arkadiusz
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-12 12:23   ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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] 172+ messages in thread

* RE: [RFC PATCH v4 3/4] dpll: documentation on DPLL subsystem interface
@ 2023-01-12 13:45       ` Kubalewski, Arkadiusz
  0 siblings, 0 replies; 172+ 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

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

^ permalink raw reply	[flat|nested] 172+ 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
  -1 siblings, 0 replies; 172+ 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

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

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-12 14:43                                         ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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] 172+ 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
  -1 siblings, 0 replies; 172+ 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

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

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-12 14:50     ` Jiri Pirko
  0 siblings, 0 replies; 172+ 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] 172+ 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 19:09     ` Jakub Kicinski
  -1 siblings, 0 replies; 172+ 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

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

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

* Re: [RFC PATCH v4 0/4] Create common DPLL/clock configuration API
@ 2023-01-12 19:09     ` Jakub Kicinski
  0 siblings, 0 replies; 172+ 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] 172+ messages in thread

end of thread, other threads:[~2023-01-12 19:22 UTC | newest]

Thread overview: 172+ 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 ` 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-29 21:37 ` [RFC PATCH v4 2/4] dpll: Add DPLL framework base functions Vadim Fedorenko
2022-11-29 21:37   ` Vadim Fedorenko
2022-11-30 15:21   ` Jiri Pirko
2022-11-30 15:21     ` Jiri Pirko
2022-11-30 16:23     ` Jiri Pirko
2022-11-30 16:23       ` Jiri Pirko
2022-12-23 16:45     ` Kubalewski, Arkadiusz
2022-12-23 16:45       ` Kubalewski, Arkadiusz
2023-01-02 12:28       ` Jiri Pirko
2023-01-02 12:28         ` Jiri Pirko
2022-11-30 16:37   ` Jiri Pirko
2022-11-30 16:37     ` Jiri Pirko
2022-12-02 11:27     ` Kubalewski, Arkadiusz
2022-12-02 11:27       ` Kubalewski, Arkadiusz
2022-12-02 12:39       ` Jiri Pirko
2022-12-02 12:39         ` Jiri Pirko
2022-12-02 14:54         ` Kubalewski, Arkadiusz
2022-12-02 14:54           ` Kubalewski, Arkadiusz
2022-12-02 16:15           ` Jiri Pirko
2022-12-02 16:15             ` Jiri Pirko
     [not found]             ` <20221202212206.3619bd5f@kernel.org>
2022-12-05 10:32               ` Jiri Pirko
2022-12-05 10:32                 ` Jiri Pirko
2022-12-06  0:19                 ` Jakub Kicinski
2022-12-06  0:19                   ` Jakub Kicinski
2022-12-06  8:50                   ` Jiri Pirko
2022-12-06  8:50                     ` Jiri Pirko
2022-12-06 17:27                     ` Jakub Kicinski
2022-12-06 17:27                       ` Jakub Kicinski
2022-12-07 13:10                       ` Jiri Pirko
2022-12-07 13:10                         ` Jiri Pirko
2022-12-07 16:59                         ` Jakub Kicinski
2022-12-07 16:59                           ` Jakub Kicinski
2022-12-08  8:14                           ` Jiri Pirko
2022-12-08  8:14                             ` Jiri Pirko
2022-12-08 16:19                             ` Jakub Kicinski
2022-12-08 16:19                               ` Jakub Kicinski
2022-12-08 16:33                               ` Jiri Pirko
2022-12-08 16:33                                 ` Jiri Pirko
2022-12-08 17:05                                 ` Jakub Kicinski
2022-12-08 17:05                                   ` Jakub Kicinski
2022-12-09  9:29                                   ` Jiri Pirko
2022-12-09  9:29                                     ` Jiri Pirko
2022-12-09 16:19                                     ` Jakub Kicinski
2022-12-09 16:19                                       ` Jakub Kicinski
2022-12-12 13:36                                       ` Jiri Pirko
2022-12-12 13:36                                         ` Jiri Pirko
2022-12-13 18:08                                         ` Kubalewski, Arkadiusz
2022-12-13 18:08                                           ` Kubalewski, Arkadiusz
2022-12-14  7:32                                           ` Jiri Pirko
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-11-29 21:37   ` Vadim Fedorenko
2022-12-02 12:43   ` kernel test robot
2022-12-19  9:13   ` Paolo Abeni
2022-12-19  9:13     ` Paolo Abeni
2023-01-12 13:45     ` Kubalewski, Arkadiusz
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-29 21:37   ` Vadim Fedorenko
2022-11-30  8:30   ` kernel test robot
2022-11-30 12:41   ` Jiri Pirko
2022-11-30 12:41     ` Jiri Pirko
2022-12-02 11:27     ` Kubalewski, Arkadiusz
2022-12-02 11:27       ` Kubalewski, Arkadiusz
2022-12-02 12:48       ` Jiri Pirko
2022-12-02 12:48         ` Jiri Pirko
2022-12-02 14:39         ` Kubalewski, Arkadiusz
2022-12-02 14:39           ` Kubalewski, Arkadiusz
2022-12-02 16:20           ` Jiri Pirko
2022-12-02 16:20             ` Jiri Pirko
2022-12-08  0:35             ` Kubalewski, Arkadiusz
2022-12-08  0:35               ` Kubalewski, Arkadiusz
2022-12-08  8:19               ` Jiri Pirko
2022-12-08  8:19                 ` Jiri Pirko
2022-12-07  2:33           ` Jakub Kicinski
2022-12-07  2:33             ` Jakub Kicinski
2022-12-07 13:19             ` Jiri Pirko
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-11-30 12:32   ` Jiri Pirko
2022-12-02 11:27   ` Kubalewski, Arkadiusz
2022-12-02 11:27     ` Kubalewski, Arkadiusz
2022-12-02 16:12     ` Jiri Pirko
2022-12-02 16:12       ` Jiri Pirko
2022-12-07  2:47       ` Jakub Kicinski
2022-12-07  2:47         ` Jakub Kicinski
2022-12-07 14:09         ` netdev.dump
2022-12-07 14:09           ` netdev.dump
2022-12-07 23:21           ` Jakub Kicinski
2022-12-07 23:21             ` Jakub Kicinski
2022-12-08 11:28             ` Jiri Pirko
2022-12-08 11:28               ` Jiri Pirko
2022-12-09  0:39               ` Jakub Kicinski
2022-12-09  0:39                 ` Jakub Kicinski
2022-12-09  0:56                 ` Kubalewski, Arkadiusz
2022-12-09  0:56                   ` Kubalewski, Arkadiusz
2022-12-08 18:08             ` Maciek Machnikowski
2022-12-08 18:08               ` Maciek Machnikowski
2022-12-09 11:07               ` Jiri Pirko
2022-12-09 11:07                 ` Jiri Pirko
2022-12-09 14:09                 ` Maciek Machnikowski
2022-12-09 14:09                   ` Maciek Machnikowski
2022-12-09 16:31                   ` Jakub Kicinski
2022-12-09 16:31                     ` Jakub Kicinski
2022-12-09 17:11                     ` Maciek Machnikowski
2022-12-09 17:11                       ` Maciek Machnikowski
2022-12-12 13:58                     ` Jiri Pirko
2022-12-12 13:58                       ` Jiri Pirko
2023-01-09 14:43                       ` Kubalewski, Arkadiusz
2023-01-09 14:43                         ` Kubalewski, Arkadiusz
2023-01-09 16:30                         ` Jiri Pirko
2023-01-09 16:30                           ` Jiri Pirko
2023-01-10 10:54                           ` Kubalewski, Arkadiusz
2023-01-10 10:54                             ` Kubalewski, Arkadiusz
2023-01-10 14:28                             ` Jiri Pirko
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:17                                 ` Kubalewski, Arkadiusz
2023-01-11 14:40                                 ` Maciek Machnikowski
2023-01-11 14:40                                   ` Maciek Machnikowski
2023-01-11 15:30                                   ` Kubalewski, Arkadiusz
2023-01-11 15:30                                     ` Kubalewski, Arkadiusz
2023-01-11 15:54                                     ` Maciek Machnikowski
2023-01-11 15:54                                       ` Maciek Machnikowski
2023-01-11 16:27                                       ` Kubalewski, Arkadiusz
2023-01-11 16:27                                         ` Kubalewski, Arkadiusz
2023-01-10 20:05                         ` Jakub Kicinski
2023-01-10 20:05                           ` Jakub Kicinski
2023-01-11  8:19                           ` Jiri Pirko
2023-01-11  8:19                             ` Jiri Pirko
2023-01-11 14:16                             ` Kubalewski, Arkadiusz
2023-01-11 14:16                               ` Kubalewski, Arkadiusz
2023-01-11 15:04                               ` Jiri Pirko
2023-01-11 15:04                                 ` Jiri Pirko
2023-01-11 15:30                                 ` Kubalewski, Arkadiusz
2023-01-11 15:30                                   ` Kubalewski, Arkadiusz
2023-01-11 16:14                                   ` Jiri Pirko
2023-01-11 16:14                                     ` Jiri Pirko
2023-01-12 12:15                                     ` Kubalewski, Arkadiusz
2023-01-12 12:15                                       ` Kubalewski, Arkadiusz
2023-01-12 14:43                                       ` Jiri Pirko
2023-01-12 14:43                                         ` Jiri Pirko
2022-12-09  0:46             ` Kubalewski, Arkadiusz
2022-12-09  0:46               ` Kubalewski, Arkadiusz
2022-12-07 14:51         ` Jiri Pirko
2022-12-07 14:51           ` Jiri Pirko
     [not found]           ` <20221207091946.3115742f@kernel.org>
2022-12-08 12:02             ` Jiri Pirko
2022-12-08 12:02               ` Jiri Pirko
2022-12-09  0:54               ` Jakub Kicinski
2022-12-08 18:23             ` Kubalewski, Arkadiusz
2022-12-08 18:23               ` Kubalewski, Arkadiusz
2022-12-08  0:27       ` Kubalewski, Arkadiusz
2022-12-08  0:27         ` Kubalewski, Arkadiusz
2022-12-08 11:58         ` Jiri Pirko
2022-12-08 11:58           ` Jiri Pirko
2022-12-08 23:05           ` Kubalewski, Arkadiusz
2022-12-08 23:05             ` Kubalewski, Arkadiusz
2022-12-09 10:01             ` Jiri Pirko
2022-12-09 10:01               ` Jiri Pirko
2023-01-12 12:23 ` Kubalewski, Arkadiusz
2023-01-12 12:23   ` Kubalewski, Arkadiusz
2023-01-12 14:50   ` Jiri Pirko
2023-01-12 14:50     ` Jiri Pirko
2023-01-12 19:09   ` Jakub Kicinski
2023-01-12 19:09     ` Jakub Kicinski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.