public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Ivan Vecera <ivecera@redhat.com>
To: netdev@vger.kernel.org
Cc: Michal Schmidt <mschmidt@redhat.com>,
	Prathosh Satish <Prathosh.Satish@microchip.com>,
	Vadim Fedorenko <vadim.fedorenko@linux.dev>,
	Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>,
	Jiri Pirko <jiri@resnulli.us>, Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>, Lee Jones <lee@kernel.org>,
	Kees Cook <kees@kernel.org>, Andy Shevchenko <andy@kernel.org>,
	Andrew Morton <akpm@linux-foundation.org>,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-hardening@vger.kernel.org
Subject: [PATCH 13/28] dpll: Add Microchip ZL3073x DPLL driver
Date: Mon,  7 Apr 2025 19:31:43 +0200	[thread overview]
Message-ID: <20250407173149.1010216-4-ivecera@redhat.com> (raw)
In-Reply-To: <20250407172836.1009461-1-ivecera@redhat.com>

Add a DPLL driver for Microchip Azurite chip family with basic
functionality where only required DPLL and pin callbacks are
implemented. Other features are added by subsequent patches.

The driver controls the sub-devices created by zl3073x MFD driver and
each DPLL sub-device represents one of the DPLL channels that are
provided by the device.

For each sub-device the driver registers a DPLL device and also input and
output pins. Number of registered pins depend on their configuration
that is stored in flash memory inside the device. The input pins can be
configured as differential or single-ended ones. If the input/output
is configured as differential then P&N pins form one input/output and only
1 input/output pin is registered. For single-ended case the number of
registered pins is up to 2 depending on whether the pin (P or N) is
enabled or disabled in configuration.

Each DPLL channel (sub-device) can drive up to 5 synthesizers whose
output is connected to up to 10 output pin pairs (P & N). Because output
pin pairs shares synthesizers where each is driven by different DPLL
channel, the driver does not support to change state of output pins.
So the output pins are registered only for DPLL device they are
connected to (based on stored configuration) and are always reported as
connected. This does not apply to the case of input pins where any of
them can be a reference for any DPLL channel.

The driver also creates a kworker task to monitor DPLL channel and input
pins changes and to notify about them DPLL core. Output pins are not
monitored as their parameters are not changed asynchronously by the
device.

Reviewed-by: Michal Schmidt <mschmidt@redhat.com>
Co-developed-by: Prathosh Satish <Prathosh.Satish@microchip.com>
Signed-off-by: Prathosh Satish <Prathosh.Satish@microchip.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 MAINTAINERS                 |    1 +
 drivers/dpll/Kconfig        |   16 +
 drivers/dpll/Makefile       |    2 +
 drivers/dpll/dpll_zl3073x.c | 1073 +++++++++++++++++++++++++++++++++++
 include/linux/mfd/zl3073x.h |    5 +
 5 files changed, 1097 insertions(+)
 create mode 100644 drivers/dpll/dpll_zl3073x.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c69a69d862310..3d542440d0b2b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15999,6 +15999,7 @@ M:	Ivan Vecera <ivecera@redhat.com>
 M:	Prathosh Satish <Prathosh.Satish@microchip.com>
 L:	netdev@vger.kernel.org
 S:	Supported
+F:	drivers/dpll/dpll_zl3073x*
 F:	drivers/mfd/zl3073x*
 F:	include/linux/mfd/zl3073x.h
 
diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
index 20607ed542435..efd867f338dfc 100644
--- a/drivers/dpll/Kconfig
+++ b/drivers/dpll/Kconfig
@@ -3,5 +3,21 @@
 # Generic DPLL drivers configuration
 #
 
+menu "DPLL support"
+
 config DPLL
 	bool
+
+config DPLL_ZL3073X
+	tristate "Microchip Azurite DPLL driver"
+	depends on MFD_ZL3073X_CORE
+	select DPLL
+	help
+	  This driver adds support for DPLL exposed by Microchip Azurite
+	  chip family.
+
+	  The devices handled by this driver are created by MFD zl3073x
+	  driver as sub-devices for each DPLL channel that is present
+	  in the device.
+
+endmenu
diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
index 2e5b278501105..9f7c99261e74d 100644
--- a/drivers/dpll/Makefile
+++ b/drivers/dpll/Makefile
@@ -7,3 +7,5 @@ obj-$(CONFIG_DPLL)      += dpll.o
 dpll-y                  += dpll_core.o
 dpll-y                  += dpll_netlink.o
 dpll-y                  += dpll_nl.o
+
+obj-$(CONFIG_DPLL_ZL3073X)	+= dpll_zl3073x.o
diff --git a/drivers/dpll/dpll_zl3073x.c b/drivers/dpll/dpll_zl3073x.c
new file mode 100644
index 0000000000000..34bd6964fe001
--- /dev/null
+++ b/drivers/dpll/dpll_zl3073x.c
@@ -0,0 +1,1073 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/dpll.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/zl3073x.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+/*
+ * Register Map Page 2, Status
+ */
+ZL3073X_REG8_IDX_DEF(ref_mon_status,		0x102,
+						ZL3073X_NUM_INPUT_PINS, 1);
+#define REF_MON_STATUS_LOS_FAIL			BIT(0)
+#define REF_MON_STATUS_SCM_FAIL			BIT(1)
+#define REF_MON_STATUS_CFM_FAIL			BIT(2)
+#define REF_MON_STATUS_GST_FAIL			BIT(3)
+#define REF_MON_STATUS_PFM_FAIL			BIT(4)
+#define REF_MON_STATUS_ESYNC_FAIL		BIT(6)
+#define REF_MON_STATUS_SPLIT_XO_FAIL		BIT(7)
+#define REF_MON_STATUS_OK			0	/* all bits zeroed */
+
+ZL3073X_REG8_IDX_DEF(dpll_mon_status,		0x110, ZL3073X_NUM_CHANNELS, 1);
+#define DPLL_MON_STATUS_LOCK			BIT(0)
+#define DPLL_MON_STATUS_HO			BIT(1)
+#define DPLL_MON_STATUS_HO_READY		BIT(2)
+
+ZL3073X_REG8_IDX_DEF(dpll_refsel_status,	0x130, ZL3073X_NUM_CHANNELS, 1);
+#define DPLL_REFSEL_STATUS_REFSEL		GENMASK(3, 0)
+#define DPLL_REFSEL_STATUS_STATE		GENMASK(6, 4)
+#define DPLL_REFSEL_STATUS_STATE_FREERUN	0
+#define DPLL_REFSEL_STATUS_STATE_HOLDOVER	1
+#define DPLL_REFSEL_STATUS_STATE_FASTLOCK	2
+#define DPLL_REFSEL_STATUS_STATE_ACQUIRING	3
+#define DPLL_REFSEL_STATUS_STATE_LOCK		4
+
+/*
+ * Register Map Page 5, DPLL
+ */
+ZL3073X_REG8_IDX_DEF(dpll_mode_refsel,		0x284, ZL3073X_NUM_CHANNELS, 4);
+#define DPLL_MODE_REFSEL_MODE			GENMASK(2, 0)
+#define DPLL_MODE_REFSEL_MODE_FREERUN		0
+#define DPLL_MODE_REFSEL_MODE_HOLDOVER		1
+#define DPLL_MODE_REFSEL_MODE_REFLOCK		2
+#define DPLL_MODE_REFSEL_MODE_AUTO		3
+#define DPLL_MODE_REFSEL_MODE_NCO		4
+#define DPLL_MODE_REFSEL_REF			GENMASK(7, 4)
+
+/*
+ * Register Map Page 9, Synth and Output
+ */
+ZL3073X_REG8_DEF(synth_phase_shift_ctrl,	0x49e);
+ZL3073X_REG8_DEF(synth_phase_shift_mask,	0x49f);
+ZL3073X_REG8_DEF(synth_phase_shift_intvl,	0x4a0);
+ZL3073X_REG16_DEF(synth_phase_shift_data,	0x4a1);
+
+/*
+ * Register Map Page 12, DPLL Mailbox
+ */
+ZL3073X_REG8_IDX_DEF(dpll_ref_prio,		0x652,
+						ZL3073X_NUM_INPUT_PINS / 2, 1);
+#define DPLL_REF_PRIO_REF_P			GENMASK(3, 0)
+#define DPLL_REF_PRIO_REF_N			GENMASK(7, 4)
+#define DPLL_REF_PRIO_MAX			14
+#define DPLL_REF_PRIO_NONE			15 /* non-selectable */
+
+#define ZL3073X_REF_NONE			ZL3073X_NUM_INPUT_PINS
+#define ZL3073X_REF_IS_VALID(_ref)		((_ref) != ZL3073X_REF_NONE)
+
+/**
+ * struct zl3073x_dpll_pin_info - DPLL pin info
+ * @props: DPLL core pin properties
+ * @package_label: pin package label
+ */
+struct zl3073x_dpll_pin_info {
+	struct dpll_pin_properties	props;
+	char				package_label[8];
+};
+
+/**
+ * struct zl3073x_dpll_pin - DPLL pin
+ * @dpll_pin: pointer to registered dpll_pin
+ * @index: index in zl3073x_dpll.pins array
+ * @prio: pin priority <0, 14>
+ * @selectable: pin is selectable in automatic mode
+ * @pin_state: last saved pin state
+ */
+struct zl3073x_dpll_pin {
+	struct dpll_pin			*dpll_pin;
+	u8				index;
+	u8				prio;
+	bool				selectable;
+	enum dpll_pin_state		pin_state;
+};
+
+/**
+ * struct zl3073x_dpll - ZL3073x DPLL sub-device structure
+ * @dev: device pointer
+ * @mfd: pointer to multi-function parent device
+ * @id: DPLL index
+ * @refsel_mode: reference selection mode
+ * @forced_ref: selected reference in forced reference lock mode
+ * @dpll_dev: pointer to registered DPLL device
+ * @lock_status: last saved DPLL lock status
+ * @pins: array of pins
+ * @kworker: thread for periodic work
+ * @work: periodic work
+ */
+struct zl3073x_dpll {
+	struct device			*dev;
+	struct zl3073x_dev		*mfd;
+	int				id;
+	u8				refsel_mode;
+	u8				forced_ref;
+	struct dpll_device		*dpll_dev;
+	enum dpll_lock_status		lock_status;
+	struct zl3073x_dpll_pin		pins[ZL3073X_NUM_PINS];
+
+	struct kthread_worker		*kworker;
+	struct kthread_delayed_work	work;
+};
+
+#define pin_to_dpll(_pin)						\
+	container_of((_pin), struct zl3073x_dpll, pins[(_pin)->index])
+
+#define pin_to_dev(_pin)						\
+	pin_to_dpll(_pin)->mfd
+
+/**
+ * zl3073x_dpll_is_input_pin - check if the pin is input one
+ * @pin: pin to check
+ *
+ * Returns true if the pin is input or false if output one.
+ */
+static bool
+zl3073x_dpll_is_input_pin(struct zl3073x_dpll_pin *pin)
+{
+	/* Output pins are stored in zl3073x_dpll.pins first and input
+	 * pins follow.
+	 */
+	if (pin->index >= ZL3073X_NUM_OUTPUT_PINS)
+		return true;
+
+	return false;
+}
+
+/**
+ * zl3073x_dpll_pin_index_get - get pin HW index
+ * @pin: pin pointer
+ *
+ * Returns index of the pin from the HW point of view.
+ */
+static u8
+zl3073x_dpll_pin_index_get(struct zl3073x_dpll_pin *pin)
+{
+	if (zl3073x_dpll_is_input_pin(pin))
+		return pin->index - ZL3073X_NUM_OUTPUT_PINS;
+
+	return pin->index;
+}
+
+/**
+ * zl3073x_dpll_is_n_pin - check if the pin is N-pin
+ * @pin: pin to check
+ *
+ * Returns true if the pin is N-pin or false if output one.
+ */
+static bool
+zl3073x_dpll_is_n_pin(struct zl3073x_dpll_pin *pin)
+{
+	/* P-pins indices are even while N-pins are odd */
+	return zl3073x_is_n_pin(zl3073x_dpll_pin_index_get(pin));
+}
+
+/**
+ * zl3073x_dpll_is_p_pin - check if the pin is P-pin
+ * @pin: pin to check
+ *
+ * Returns true if the pin is P-pin or false if output one.
+ */
+static bool
+zl3073x_dpll_is_p_pin(struct zl3073x_dpll_pin *pin)
+{
+	return zl3073x_is_p_pin(zl3073x_dpll_pin_index_get(pin));
+}
+
+/**
+ * zl3073x_dpll_output_pin_output_get - get output index for given output pin
+ * @pin: pointer to pin
+ *
+ * Returns output index for the given output pin
+ */
+static u8
+zl3073x_dpll_output_pin_output_get(struct zl3073x_dpll_pin *pin)
+{
+	WARN_ON(zl3073x_dpll_is_input_pin(pin));
+
+	return zl3073x_dpll_pin_index_get(pin) / 2;
+}
+
+static int
+zl3073x_dpll_pin_direction_get(const struct dpll_pin *dpll_pin, void *pin_priv,
+			       const struct dpll_device *dpll, void *dpll_priv,
+			       enum dpll_pin_direction *direction,
+			       struct netlink_ext_ack *extack)
+{
+	struct zl3073x_dpll_pin *pin = pin_priv;
+
+	if (zl3073x_dpll_is_input_pin(pin))
+		*direction = DPLL_PIN_DIRECTION_INPUT;
+	else
+		*direction = DPLL_PIN_DIRECTION_OUTPUT;
+
+	return 0;
+}
+
+/**
+ * zl3073x_dpll_selected_ref_get - get currently selected reference
+ * @zldpll: pointer to zl3073x_dpll
+ * @ref: place to store selected reference
+ *
+ * Check for currently selected reference the DPLL should be locked to
+ * and stores its index to given @ref.
+ *
+ * Return 0 in case of success or negative value otherwise.
+ */
+static int
+zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
+{
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	u8 state, value;
+	int rc;
+
+	switch (zldpll->refsel_mode) {
+	case DPLL_MODE_REFSEL_MODE_AUTO:
+		/* For automatic mode read refsel_status register */
+		rc = zl3073x_read_dpll_refsel_status(zldev, zldpll->id, &value);
+		if (rc)
+			return rc;
+
+		/* Extract reference state */
+		state = FIELD_GET(DPLL_REFSEL_STATUS_STATE, value);
+
+		/* Return the reference only if the DPLL is locked to it */
+		if (state == DPLL_REFSEL_STATUS_STATE_LOCK)
+			*ref = FIELD_GET(DPLL_REFSEL_STATUS_REFSEL, value);
+		else
+			*ref = ZL3073X_REF_NONE;
+		break;
+	case DPLL_MODE_REFSEL_MODE_REFLOCK:
+		/* For manual mode return stored value */
+		*ref = zldpll->forced_ref;
+		break;
+	default:
+		/* For other modes like NCO, freerun... there is no input ref */
+		*ref = ZL3073X_REF_NONE;
+		break;
+	}
+
+	return 0;
+}
+
+/**
+ * zl3073x_dpll_connected_ref_get - get currently connected reference
+ * @zldpll: pointer to zl3073x_dpll
+ * @ref: place to store selected reference
+ *
+ * Looks for currently connected the DPLL is locked to and stores its index
+ * to given @ref.
+ *
+ * Return 0 in case of success or negative value otherwise.
+ */
+static int
+zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
+{
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	int rc;
+
+	/* Get currently selected input reference */
+	rc = zl3073x_dpll_selected_ref_get(zldpll, ref);
+	if (rc)
+		return rc;
+
+	if (ZL3073X_REF_IS_VALID(*ref)) {
+		u8 ref_status;
+
+		/* Read the reference monitor status */
+		rc = zl3073x_read_ref_mon_status(zldev, *ref, &ref_status);
+		if (rc)
+			return rc;
+
+		/* If the monitor indicates an error nothing is connected */
+		if (ref_status != REF_MON_STATUS_OK)
+			*ref = ZL3073X_REF_NONE;
+	}
+
+	return 0;
+}
+
+/**
+ * zl3073x_dpll_ref_prio_get - get priority for given input pin
+ * @pin: pointer to pin
+ * @prio: place to store priority
+ *
+ * Reads current priority for the given input pin and stores the value
+ * to @prio.
+ *
+ * Returns 0 in case of success or negative value otherwise.
+ */
+static int
+zl3073x_dpll_ref_prio_get(struct zl3073x_dpll_pin *pin, u8 *prio)
+{
+	struct zl3073x_dpll *zldpll = pin_to_dpll(pin);
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	u8 ref_id, ref_prio;
+	int rc;
+
+	/* Read DPLL configuration into mailbox */
+	rc = zl3073x_mb_dpll_read(zldev, zldpll->id);
+	if (rc)
+		return rc;
+
+	/* Get index of the pin */
+	ref_id = zl3073x_dpll_pin_index_get(pin);
+
+	/* Read ref prio nibble */
+	rc = zl3073x_read_dpll_ref_prio(zldev, ref_id / 2, &ref_prio);
+	if (rc)
+		return rc;
+
+	/* Select nibble according pin type */
+	if (zl3073x_dpll_is_p_pin(pin))
+		*prio = FIELD_GET(DPLL_REF_PRIO_REF_P, ref_prio);
+	else
+		*prio = FIELD_GET(DPLL_REF_PRIO_REF_N, ref_prio);
+
+	return rc;
+}
+
+static int
+zl3073x_dpll_input_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
+					 void *pin_priv,
+					 const struct dpll_device *dpll,
+					 void *dpll_priv,
+					 enum dpll_pin_state *state,
+					 struct netlink_ext_ack *extack)
+{
+	struct zl3073x_dpll *zldpll = dpll_priv;
+	struct zl3073x_dpll_pin *pin = pin_priv;
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	u8 ref_id, ref_conn, ref_status;
+	int rc;
+
+	guard(zl3073x)(zldev);
+
+	/* Get index of the pin */
+	ref_id = zl3073x_dpll_pin_index_get(pin);
+
+	/* Get currently connected reference */
+	rc = zl3073x_dpll_connected_ref_get(zldpll, &ref_conn);
+	if (rc)
+		return rc;
+
+	if (ref_id == ref_conn) {
+		*state = DPLL_PIN_STATE_CONNECTED;
+		return 0;
+	}
+
+	/* If the DPLL is running in automatic mode and the reference is
+	 * selectable and its monitor does not report any error then report
+	 * pin as selectable.
+	 */
+	if (zldpll->refsel_mode == DPLL_MODE_REFSEL_MODE_AUTO &&
+	    pin->selectable) {
+		/* Read reference monitor status */
+		rc = zl3073x_read_ref_mon_status(zldev, ref_id, &ref_status);
+		if (rc)
+			return rc;
+
+		/* If the monitor indicates errors report the reference
+		 * as disconnected
+		 */
+		if (ref_status == REF_MON_STATUS_OK) {
+			*state = DPLL_PIN_STATE_SELECTABLE;
+			return 0;
+		}
+	}
+
+	/* Otherwise report the pin as disconnected */
+	*state = DPLL_PIN_STATE_DISCONNECTED;
+
+	return 0;
+}
+
+static int
+zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
+					  void *pin_priv,
+					  const struct dpll_device *dpll,
+					  void *dpll_priv,
+					  enum dpll_pin_state *state,
+					  struct netlink_ext_ack *extack)
+{
+	/* If the output pin is registered then it is always connected */
+	*state = DPLL_PIN_STATE_CONNECTED;
+
+	return 0;
+}
+
+static int
+zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
+			     enum dpll_lock_status *status,
+			     enum dpll_lock_status_error *status_error,
+			     struct netlink_ext_ack *extack)
+{
+	struct zl3073x_dpll *zldpll = dpll_priv;
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	u8 mon_status;
+	int rc;
+
+	guard(zl3073x)(zldev);
+
+	rc = zl3073x_read_dpll_mon_status(zldev, zldpll->id, &mon_status);
+
+	if (rc)
+		return rc;
+
+	if (FIELD_GET(DPLL_MON_STATUS_LOCK, mon_status)) {
+		if (FIELD_GET(DPLL_MON_STATUS_HO_READY, mon_status))
+			*status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;
+		else
+			*status = DPLL_LOCK_STATUS_LOCKED;
+	} else if (FIELD_GET(DPLL_MON_STATUS_HO, mon_status)) {
+		*status = DPLL_LOCK_STATUS_HOLDOVER;
+	} else {
+		*status = DPLL_LOCK_STATUS_UNLOCKED;
+	}
+
+	return rc;
+}
+
+static int
+zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
+		      enum dpll_mode *mode, struct netlink_ext_ack *extack)
+{
+	struct zl3073x_dpll *zldpll = dpll_priv;
+
+	switch (zldpll->refsel_mode) {
+	case DPLL_MODE_REFSEL_MODE_FREERUN:
+	case DPLL_MODE_REFSEL_MODE_HOLDOVER:
+	case DPLL_MODE_REFSEL_MODE_NCO:
+	case DPLL_MODE_REFSEL_MODE_REFLOCK:
+		/* Use MANUAL for device FREERUN, HOLDOVER, NCO and
+		 * REFLOCK modes
+		 */
+		*mode = DPLL_MODE_MANUAL;
+		break;
+	case DPLL_MODE_REFSEL_MODE_AUTO:
+		/* Use AUTO for device AUTO mode */
+		*mode = DPLL_MODE_AUTOMATIC;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
+	.direction_get = zl3073x_dpll_pin_direction_get,
+	.state_on_dpll_get = zl3073x_dpll_input_pin_state_on_dpll_get,
+};
+
+static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
+	.direction_get = zl3073x_dpll_pin_direction_get,
+	.state_on_dpll_get = zl3073x_dpll_output_pin_state_on_dpll_get,
+};
+
+static const struct dpll_device_ops zl3073x_dpll_device_ops = {
+	.lock_status_get = zl3073x_dpll_lock_status_get,
+	.mode_get = zl3073x_dpll_mode_get,
+};
+
+/**
+ * zl3073x_dpll_pin_info_package_label_set - generate package label for the pin
+ * @pin: pointer to pin
+ * @pin_info: pointer to pin info structure
+ *
+ * Generates package label string and stores it into pin info structure.
+ *
+ * Possible formats:
+ * REF<n> - differential input reference
+ * REF<n>P & REF<n>N - single-ended input reference (P or N pin)
+ * OUT<n> - differential output
+ * OUT<n>P & OUT<n>N - single-ended output (P or N pin)
+ */
+static void
+zl3073x_dpll_pin_info_package_label_set(struct zl3073x_dpll_pin *pin,
+					struct zl3073x_dpll_pin_info *pin_info)
+{
+	struct zl3073x_dev *zldev = pin_to_dpll(pin)->mfd;
+	char suffix;
+	u8 idx;
+
+	suffix = zl3073x_dpll_is_p_pin(pin) ? 'P' : 'N';
+
+	if (zl3073x_dpll_is_input_pin(pin)) {
+		idx = zl3073x_dpll_pin_index_get(pin);
+
+		if (zl3073x_input_is_diff(zldev, idx))
+			/* For differential use REF<n> */
+			snprintf(pin_info->package_label,
+				 sizeof(pin_info->package_label),
+				 "REF%u", idx / 2);
+		else
+			/* For single-ended use REF<n>P/N */
+			snprintf(pin_info->package_label,
+				 sizeof(pin_info->package_label),
+				 "REF%u%c", idx / 2, suffix);
+	} else {
+		idx = zl3073x_dpll_output_pin_output_get(pin);
+
+		switch (zl3073x_output_signal_format_get(zldev, idx)) {
+		case OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
+		case OUTPUT_MODE_SIGNAL_FORMAT_DIFFERENTIAL:
+		case OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
+			/* For differential use OUT<n> */
+			snprintf(pin_info->package_label,
+				 sizeof(pin_info->package_label), "OUT%u", idx);
+			break;
+		default:
+			/* For single-ended use OUT<n>P/N */
+			snprintf(pin_info->package_label,
+				 sizeof(pin_info->package_label), "OUT%u%c",
+				 idx, suffix);
+			break;
+		}
+	}
+
+	/* Set package_label pointer in DPLL core properties to generated
+	 * string.
+	 */
+	pin_info->props.package_label = pin_info->package_label;
+}
+
+/**
+ * zl3073x_dpll_pin_info_get - get pin info
+ * @pin: pin whose info is returned
+ *
+ * The function allocates pin info structure, generates package label
+ * string according pin type and its order number.
+ *
+ * Returns pointer to allocated pin info structure that has to be freed
+ * by @zl3073x_dpll_pin_info_put by the caller and in case of error
+ * then error pointer is returned.
+ */
+static struct zl3073x_dpll_pin_info *
+zl3073x_dpll_pin_info_get(struct zl3073x_dpll_pin *pin)
+{
+	struct zl3073x_dpll_pin_info *pin_info;
+
+	/* Allocate pin info structure */
+	pin_info = kzalloc(sizeof(*pin_info), GFP_KERNEL);
+	if (!pin_info)
+		return ERR_PTR(-ENOMEM);
+
+	/* Set pin type */
+	if (zl3073x_dpll_is_input_pin(pin))
+		pin_info->props.type = DPLL_PIN_TYPE_EXT;
+	else
+		pin_info->props.type = DPLL_PIN_TYPE_GNSS;
+
+	pin_info->props.phase_range.min = S32_MIN;
+	pin_info->props.phase_range.max = S32_MAX;
+
+	/* Generate package label for the given pin */
+	zl3073x_dpll_pin_info_package_label_set(pin, pin_info);
+
+	return pin_info;
+}
+
+/**
+ * zl3073x_dpll_pin_info_put - free pin info
+ * @pin_info: pin info to free
+ *
+ * The function deallocates given pin info structure.
+ */
+static void
+zl3073x_dpll_pin_info_put(struct zl3073x_dpll_pin_info *pin_info)
+{
+	/* Free the pin info structure itself */
+	kfree(pin_info);
+}
+
+static int
+zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin)
+{
+	struct zl3073x_dpll *zldpll = pin_to_dpll(pin);
+	struct zl3073x_dpll_pin_info *pin_info;
+	const struct dpll_pin_ops *ops;
+	int rc;
+
+	/* Get pin info */
+	pin_info = zl3073x_dpll_pin_info_get(pin);
+	if (IS_ERR(pin_info))
+		return PTR_ERR(pin_info);
+
+	/* Create or get existing DPLL pin */
+	pin->dpll_pin = dpll_pin_get(zldpll->mfd->clock_id, pin->index,
+				     THIS_MODULE, &pin_info->props);
+	if (IS_ERR(pin->dpll_pin)) {
+		rc = PTR_ERR(pin->dpll_pin);
+		goto err_pin_get;
+	}
+
+	if (zl3073x_dpll_is_input_pin(pin))
+		ops = &zl3073x_dpll_input_pin_ops;
+	else
+		ops = &zl3073x_dpll_output_pin_ops;
+
+	/* Register the pin */
+	rc = dpll_pin_register(zldpll->dpll_dev, pin->dpll_pin, ops, pin);
+	if (rc)
+		goto err_register;
+
+	/* Free pin info */
+	zl3073x_dpll_pin_info_put(pin_info);
+
+	return 0;
+
+err_register:
+	dpll_pin_put(pin->dpll_pin);
+	pin->dpll_pin = NULL;
+err_pin_get:
+	zl3073x_dpll_pin_info_put(pin_info);
+
+	return rc;
+}
+
+static void
+zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin)
+{
+	struct zl3073x_dpll *zldpll = pin_to_dpll(pin);
+	const struct dpll_pin_ops *ops;
+
+	if (IS_ERR_OR_NULL(pin->dpll_pin))
+		return;
+
+	if (zl3073x_dpll_is_input_pin(pin))
+		ops = &zl3073x_dpll_input_pin_ops;
+	else
+		ops = &zl3073x_dpll_output_pin_ops;
+
+	/* Unregister the pin */
+	dpll_pin_unregister(zldpll->dpll_dev, pin->dpll_pin, ops, pin);
+
+	dpll_pin_put(pin->dpll_pin);
+	pin->dpll_pin = NULL;
+}
+
+static int
+zl3073x_dpll_register_input_pin(struct zl3073x_dpll_pin *pin)
+{
+	struct zl3073x_dpll *zldpll = pin_to_dpll(pin);
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	u8 ref;
+
+	/* Get index of the pin */
+	ref = zl3073x_dpll_pin_index_get(pin);
+
+	/* If the ref is differential then register only for the P-pin */
+	if (zl3073x_input_is_diff(zldev, ref) && zl3073x_dpll_is_n_pin(pin)) {
+		dev_dbg(zldev->dev, "INPUT%u is differential, skipping N-pin\n",
+			ref);
+		return 0;
+	}
+
+	/* If the ref is disabled then skip registration */
+	if (!zl3073x_input_is_enabled(zldev, ref)) {
+		dev_dbg(zldev->dev, "INPUT%u is disabled\n", ref);
+		return 0;
+	}
+
+	scoped_guard(zl3073x, zldev) {
+		int rc;
+
+		rc = zl3073x_dpll_ref_prio_get(pin, &pin->prio);
+		if (rc)
+			return rc;
+	}
+
+	if (pin->prio == DPLL_REF_PRIO_NONE) {
+		/* Clamp priority to max value and make pin non-selectable */
+		pin->prio = DPLL_REF_PRIO_MAX;
+		pin->selectable = false;
+	} else {
+		/* Mark pin as selectable */
+		pin->selectable = true;
+	}
+
+	/* Register the pin */
+	return zl3073x_dpll_pin_register(pin);
+}
+
+static int
+zl3073x_dpll_register_output_pin(struct zl3073x_dpll_pin *pin)
+{
+	struct zl3073x_dpll *zldpll = pin_to_dpll(pin);
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	u8 dpll, output, synth;
+
+	/* Get output id for the pin and synth where it is connected to */
+	output = zl3073x_dpll_output_pin_output_get(pin);
+	synth = zl3073x_output_synth_get(zldev, output);
+
+	/* Get DPLL channel the synth is associated with */
+	dpll = zl3073x_synth_dpll_get(zldev, synth);
+
+	/* If the output's synth is connected to different DPLL channel
+	 * then skip registration.
+	 */
+	if (dpll != zldpll->id) {
+		dev_dbg(zldev->dev, "OUTPUT%u is driven by different DPLL\n",
+			output);
+		return 0;
+	}
+
+	/* If the output is disabled then skip registration */
+	if (!zl3073x_output_is_enabled(zldev, output)) {
+		dev_dbg(zldev->dev, "OUTPUT%u is disabled\n", output);
+		return 0;
+	}
+
+	/* Check ouput's signal format */
+	switch (zldev->output[output].signal_format) {
+	case OUTPUT_MODE_SIGNAL_FORMAT_DISABLED:
+		/* Output is disabled, nothing to register */
+		dev_dbg(zldev->dev, "OUTPUT%u is disabled by signal format\n",
+			output);
+		return 0;
+
+	case OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
+	case OUTPUT_MODE_SIGNAL_FORMAT_DIFFERENTIAL:
+	case OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
+		/* Output is differential, skip registration for N-pin */
+		if (zl3073x_dpll_is_n_pin(pin)) {
+			dev_dbg(zldev->dev,
+				"OUTPUT%u is differential, skipping N-pin\n",
+				output);
+			return 0;
+		}
+		break;
+
+	case OUTPUT_MODE_SIGNAL_FORMAT_TWO:
+	case OUTPUT_MODE_SIGNAL_FORMAT_TWO_INV:
+	case OUTPUT_MODE_SIGNAL_FORMAT_TWO_N_DIV:
+	case OUTPUT_MODE_SIGNAL_FORMAT_TWO_N_DIV_INV:
+		/* Output is two single ended outputs, continue with
+		 * registration.
+		 */
+		break;
+
+	case OUTPUT_MODE_SIGNAL_FORMAT_ONE_P:
+		/* Output is one single ended P-pin output */
+		if (zl3073x_dpll_is_n_pin(pin)) {
+			dev_dbg(zldev->dev,
+				"OUTPUT%u is P-pin only, skipping N-pin\n",
+				output);
+			return 0;
+		}
+		break;
+	case OUTPUT_MODE_SIGNAL_FORMAT_ONE_N:
+		/* Output is one single ended N-pin output */
+		if (zl3073x_dpll_is_p_pin(pin)) {
+			dev_dbg(zldev->dev,
+				"OUTPUT%u is N-pin only, skipping P-pin\n",
+				output);
+			return 0;
+		}
+		break;
+	default:
+		dev_warn(zldev->dev, "Unknown output mode signal format: %u\n",
+			 zldev->output[output].signal_format);
+		return 0;
+	}
+
+	/* Register the pin */
+	return zl3073x_dpll_pin_register(pin);
+}
+
+static int
+zl3073x_dpll_register_pins(struct zl3073x_dpll *zldpll)
+{
+	int i, rc;
+
+	for (i = 0; i < ZL3073X_NUM_PINS; i++) {
+		struct zl3073x_dpll_pin *pin = &zldpll->pins[i];
+
+		pin->index = i;
+
+		if (zl3073x_dpll_is_input_pin(pin))
+			rc = zl3073x_dpll_register_input_pin(pin);
+		else
+			rc = zl3073x_dpll_register_output_pin(pin);
+
+		if (rc)
+			goto err_register;
+	}
+
+	return 0;
+
+err_register:
+	while (i--)
+		zl3073x_dpll_pin_unregister(&zldpll->pins[i]);
+
+	return rc;
+}
+
+static void
+zl3073x_dpll_unregister_pins(struct zl3073x_dpll *zldpll)
+{
+	int i;
+
+	for (i = 0; i < ZL3073X_NUM_PINS; i++)
+		zl3073x_dpll_pin_unregister(&zldpll->pins[i]);
+}
+
+static int
+zl3073x_dpll_register(struct zl3073x_dpll *zldpll)
+{
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	int rc;
+
+	scoped_guard(zl3073x, zldev) {
+		u8 dpll_mode_refsel;
+
+		/* Read DPLL mode and forcibly selected reference */
+		rc = zl3073x_read_dpll_mode_refsel(zldev, zldpll->id,
+						   &dpll_mode_refsel);
+		if (rc)
+			return rc;
+
+		/* Extract mode and selected input reference */
+		zldpll->refsel_mode = FIELD_GET(DPLL_MODE_REFSEL_MODE,
+						dpll_mode_refsel);
+		zldpll->forced_ref = FIELD_GET(DPLL_MODE_REFSEL_REF,
+					       dpll_mode_refsel);
+	}
+
+	zldpll->dpll_dev = dpll_device_get(zldev->clock_id, zldpll->id,
+					   THIS_MODULE);
+	if (IS_ERR(zldpll->dpll_dev))
+		return PTR_ERR(zldpll->dpll_dev);
+
+	rc = dpll_device_register(zldpll->dpll_dev, DPLL_TYPE_PPS,
+				  &zl3073x_dpll_device_ops, zldpll);
+	if (rc) {
+		dpll_device_put(zldpll->dpll_dev);
+		zldpll->dpll_dev = NULL;
+	}
+
+	return rc;
+}
+
+static void
+zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll)
+{
+	if (IS_ERR_OR_NULL(zldpll->dpll_dev))
+		return;
+
+	dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
+			       zldpll);
+	dpll_device_put(zldpll->dpll_dev);
+	zldpll->dpll_dev = NULL;
+}
+
+static int
+zl3073x_dpll_init(struct zl3073x_dpll *zldpll)
+{
+	int rc;
+
+	rc = zl3073x_dpll_register(zldpll);
+	if (rc)
+		return rc;
+
+	rc = zl3073x_dpll_register_pins(zldpll);
+	if (rc)
+		zl3073x_dpll_unregister(zldpll);
+
+	return rc;
+}
+
+static void
+zl3073x_dpll_periodic_work(struct kthread_work *work)
+{
+	struct zl3073x_dpll *zldpll = container_of(work, struct zl3073x_dpll,
+						   work.work);
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	enum dpll_lock_status lock_status;
+	int i, rc;
+
+	/* Get current lock status for the DPLL */
+	rc = zl3073x_dpll_lock_status_get(zldpll->dpll_dev, zldpll,
+					  &lock_status, NULL, NULL);
+	if (rc) {
+		dev_err(zldpll->mfd->dev,
+			"Failed to get DPLL lock status: %pe", ERR_PTR(rc));
+		goto out;
+	}
+
+	/* If lock status was changed then notify DPLL core */
+	if (zldpll->lock_status != lock_status) {
+		zldpll->lock_status = lock_status;
+		dpll_device_change_ntf(zldpll->dpll_dev);
+	}
+
+	/* Output pins change checks are not necessary because output states
+	 * are constant.
+	 */
+	for (i = 0; i < ZL3073X_NUM_INPUT_PINS; i++) {
+		struct zl3073x_dpll_pin *pin;
+		enum dpll_pin_state state;
+
+		/* Input pins starts are stored after output pins */
+		pin = &zldpll->pins[ZL3073X_NUM_OUTPUT_PINS + i];
+
+		/* Skip non-registered pins */
+		if (!pin->dpll_pin)
+			continue;
+
+		rc = zl3073x_dpll_input_pin_state_on_dpll_get(pin->dpll_pin,
+							      pin,
+							      zldpll->dpll_dev,
+							      zldpll, &state,
+							      NULL);
+		if (rc)
+			goto out;
+
+		if (state != pin->pin_state) {
+			dev_dbg(zldev->dev,
+				"INPUT%u state changed to %u\n",
+				zl3073x_dpll_pin_index_get(pin), state);
+			pin->pin_state = state;
+			dpll_pin_change_ntf(pin->dpll_pin);
+		}
+	}
+
+out:
+	/* Run twice a second */
+	kthread_queue_delayed_work(zldpll->kworker, &zldpll->work,
+				   msecs_to_jiffies(500));
+}
+
+static int
+zl3073x_dpll_init_worker(struct zl3073x_dpll *zldpll)
+{
+	struct kthread_worker *kworker;
+
+	kthread_init_delayed_work(&zldpll->work, zl3073x_dpll_periodic_work);
+	kworker = kthread_run_worker(0, "zl3073x-%s", dev_name(zldpll->dev));
+	if (IS_ERR(kworker))
+		return PTR_ERR(kworker);
+
+	zldpll->kworker = kworker;
+	kthread_queue_delayed_work(zldpll->kworker, &zldpll->work, 0);
+
+	return 0;
+}
+
+static int
+zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dpll *zldpll)
+{
+	struct zl3073x_dev *zldev = zldpll->mfd;
+	int rc;
+
+	guard(zl3073x)(zldpll->mfd);
+
+	rc = zl3073x_write_synth_phase_shift_mask(zldev, 0x1f);
+	if (rc)
+		return rc;
+
+	rc = zl3073x_write_synth_phase_shift_intvl(zldev, 0x01);
+	if (rc)
+		return rc;
+
+	rc = zl3073x_write_synth_phase_shift_data(zldev, 0xffff);
+	if (rc)
+		return rc;
+
+	rc = zl3073x_write_synth_phase_shift_ctrl(zldev, 0x01);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+static int
+zl3073x_dpll_probe(struct platform_device *pdev)
+{
+	struct zl3073x_dpll *zldpll;
+	int rc;
+
+	zldpll = devm_kzalloc(&pdev->dev, sizeof(*zldpll), GFP_KERNEL);
+	if (!zldpll)
+		return -ENOMEM;
+
+	zldpll->dev = &pdev->dev;
+	zldpll->mfd = dev_get_drvdata(pdev->dev.parent);
+	zldpll->id = pdev->mfd_cell->id;
+
+	rc = zl3073x_dpll_init(zldpll);
+	if (rc)
+		return rc;
+
+	rc = zl3073x_dpll_init_worker(zldpll);
+	if (rc)
+		goto err_init_worker;
+
+	platform_set_drvdata(pdev, zldpll);
+
+	/* Initial firmware fine phase correction */
+	rc = zl3073x_dpll_init_fine_phase_adjust(zldpll);
+	if (rc)
+		goto err_init_phase_adjust;
+
+	return rc;
+
+err_init_phase_adjust:
+	kthread_cancel_delayed_work_sync(&zldpll->work);
+	kthread_destroy_worker(zldpll->kworker);
+err_init_worker:
+	zl3073x_dpll_unregister_pins(zldpll);
+	zl3073x_dpll_unregister(zldpll);
+
+	return rc;
+}
+
+static void
+zl3073x_dpll_remove(struct platform_device *pdev)
+{
+	struct zl3073x_dpll *zldpll = platform_get_drvdata(pdev);
+
+	/* Stop worker */
+	kthread_cancel_delayed_work_sync(&zldpll->work);
+	kthread_destroy_worker(zldpll->kworker);
+
+	/* Unregister all pins and dpll */
+	zl3073x_dpll_unregister_pins(zldpll);
+	zl3073x_dpll_unregister(zldpll);
+}
+
+static const struct platform_device_id zl3073x_dpll_platform_id[] = {
+	{ "zl3073x-dpll", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, zl3073x_dpll_platform_id);
+
+static struct platform_driver zl3073x_dpll_driver = {
+	.driver = {
+		.name = "zl3073x-dpll",
+	},
+	.probe = zl3073x_dpll_probe,
+	.remove	= zl3073x_dpll_remove,
+	.id_table = zl3073x_dpll_platform_id,
+};
+
+module_platform_driver(zl3073x_dpll_driver);
+
+MODULE_AUTHOR("Ivan Vecera <ivecera@redhat.com>");
+MODULE_AUTHOR("Tariq Haddad <tariq.haddad@microchip.com>");
+MODULE_DESCRIPTION("Microchip ZL3073x DPLL driver");
+MODULE_IMPORT_NS("ZL3073X");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/zl3073x.h b/include/linux/mfd/zl3073x.h
index 825e6706dc974..3eaa5d96ca9af 100644
--- a/include/linux/mfd/zl3073x.h
+++ b/include/linux/mfd/zl3073x.h
@@ -9,9 +9,14 @@
 /*
  * Hardware limits for ZL3073x chip family
  */
+#define ZL3073X_NUM_CHANNELS	2
 #define ZL3073X_NUM_INPUTS	10
 #define ZL3073X_NUM_OUTPUTS	10
 #define ZL3073X_NUM_SYNTHS	5
+#define ZL3073X_NUM_INPUT_PINS	ZL3073X_NUM_INPUTS
+#define ZL3073X_NUM_OUTPUT_PINS	(ZL3073X_NUM_OUTPUTS * 2)
+#define ZL3073X_NUM_PINS	(ZL3073X_NUM_INPUT_PINS + \
+				 ZL3073X_NUM_OUTPUT_PINS)
 
 struct zl3073x_input {
 	bool	enabled;
-- 
2.48.1


  parent reply	other threads:[~2025-04-07 17:32 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-07 17:28 [PATCH 00/28] Add Microchip ZL3073x support Ivan Vecera
2025-04-07 17:28 ` [PATCH 01/28] mfd: " Ivan Vecera
2025-04-07 17:53   ` Krzysztof Kozlowski
2025-04-09  6:31     ` Ivan Vecera
2025-04-07 18:05   ` Andy Shevchenko
2025-04-09  6:40     ` Ivan Vecera
2025-04-14  6:36       ` Andy Shevchenko
2025-04-14 11:39         ` Ivan Vecera
2025-04-14 11:52           ` Ivan Vecera
2025-04-14 13:55             ` Andy Shevchenko
2025-04-14 14:08               ` Ivan Vecera
2025-04-14 14:07             ` Ivan Vecera
2025-04-14 14:10               ` Andy Shevchenko
2025-04-14 14:13                 ` Andy Shevchenko
2025-04-14 14:16                   ` Andy Shevchenko
2025-04-14 14:53                     ` Ivan Vecera
2025-04-14 17:09                       ` Andy Shevchenko
2025-04-14 17:42                         ` Ivan Vecera
2025-04-14 13:58           ` Andy Shevchenko
2025-04-07 17:28 ` [PATCH 02/28] mfd: zl3073x: Register itself as devlink device Ivan Vecera
2025-04-07 20:57   ` Andrew Lunn
2025-04-09  6:41     ` Ivan Vecera
2025-04-07 17:28 ` [PATCH 03/28] mfd: zl3073x: Add register access helpers Ivan Vecera
2025-04-07 21:03   ` Andrew Lunn
2025-04-09  6:42     ` Ivan Vecera
2025-04-07 17:28 ` [PATCH 04/28] mfd: zl3073x: Add macros for device registers access Ivan Vecera
2025-04-07 17:28 ` [PATCH 05/28] mfd: zl3073x: Add components versions register defs Ivan Vecera
2025-04-07 21:09   ` Andrew Lunn
2025-04-09  6:44     ` Ivan Vecera
2025-04-10  7:11       ` Krzysztof Kozlowski
2025-04-10 10:23         ` Ivan Vecera
2025-04-10 10:42           ` Krzysztof Kozlowski
2025-04-10 12:01             ` Ivan Vecera
2025-04-07 17:28 ` [PATCH 06/28] mfd: zl3073x: Implement devlink device info Ivan Vecera
2025-04-07 17:28 ` [PATCH 07/28] mfd: zl3073x: Add macro to wait for register value bits to be cleared Ivan Vecera
2025-04-07 17:28 ` [PATCH 08/28] mfd: zl3073x: Add functions to work with register mailboxes Ivan Vecera
2025-04-07 17:28 ` [PATCH 09/28] mfd: zl3073x: Add clock_id field Ivan Vecera
2025-04-07 17:31 ` [PATCH 10/28] lib: Allow modules to use strnchrnul Ivan Vecera
2025-04-07 17:50   ` Kees Cook
2025-04-07 17:31 ` [PATCH 11/28] mfd: zl3073x: Load mfg file into HW if it is present Ivan Vecera
2025-04-07 17:31 ` [PATCH 12/28] mfd: zl3073x: Fetch invariants during probe Ivan Vecera
2025-04-07 17:31 ` Ivan Vecera [this message]
2025-04-07 17:31 ` [PATCH 14/28] mfd: zl3073x: Register DPLL sub-device during init Ivan Vecera
2025-04-07 17:31 ` [PATCH 15/28] dt-bindings: dpll: Add device tree bindings for DPLL device and pin Ivan Vecera
2025-04-07 18:01   ` Krzysztof Kozlowski
2025-04-07 18:10     ` Krzysztof Kozlowski
2025-04-08  5:19     ` Michal Schmidt
2025-04-09  7:09     ` Ivan Vecera
2025-04-07 17:31 ` [PATCH 16/28] dt-bindings: dpll: Add support for Microchip Azurite chip family Ivan Vecera
2025-04-07 18:04   ` Krzysztof Kozlowski
2025-04-09  7:19     ` Ivan Vecera
2025-04-10  7:01       ` Krzysztof Kozlowski
2025-04-10 10:28         ` Ivan Vecera
2025-04-10 12:19           ` Krzysztof Kozlowski
2025-04-10 12:38             ` Ivan Vecera
2025-04-07 17:31 ` [PATCH 17/28] dpll: zl3073x: Read DPLL types from firmware Ivan Vecera
2025-04-07 17:31 ` [PATCH 18/28] dpll: zl3073x: Read optional pin properties " Ivan Vecera
2025-04-07 18:06   ` Krzysztof Kozlowski
2025-04-09  7:22     ` Ivan Vecera
2025-04-07 17:31 ` [PATCH 19/28] dpll: zl3073x: Implement input pin selection in manual mode Ivan Vecera
2025-04-07 17:32 ` [PATCH 20/28] dpll: zl3073x: Add support to get/set priority on input pins Ivan Vecera
2025-04-07 17:32 ` [PATCH 21/28] dpll: zl3073x: Implement input pin state setting in automatic mode Ivan Vecera
2025-04-07 17:32 ` [PATCH 22/28] dpll: zl3073x: Add support to get/set frequency on input pins Ivan Vecera
2025-04-07 17:32 ` [PATCH 23/28] dpll: zl3073x: Add support to get/set frequency on output pins Ivan Vecera
2025-04-07 17:32 ` [PATCH 24/28] dpll: zl3073x: Read pin supported frequencies from firmware Ivan Vecera
2025-04-07 17:32 ` [PATCH 25/28] dpll: zl3073x: Add support to get phase offset on input pins Ivan Vecera
2025-04-07 17:32 ` [PATCH 26/28] dpll: zl3073x: Add support to get/set phase adjust on pins Ivan Vecera
2025-04-07 17:33 ` [PATCH 27/28] dpll: zl3073x: Add support to get/set esync " Ivan Vecera
2025-04-07 17:33 ` [PATCH 28/28] dpll: zl3073x: Add support to get fractional frequency offset on input pins Ivan Vecera
2025-04-07 18:06 ` [PATCH 00/28] Add Microchip ZL3073x support Andy Shevchenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250407173149.1010216-4-ivecera@redhat.com \
    --to=ivecera@redhat.com \
    --cc=Prathosh.Satish@microchip.com \
    --cc=akpm@linux-foundation.org \
    --cc=andy@kernel.org \
    --cc=arkadiusz.kubalewski@intel.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jiri@resnulli.us \
    --cc=kees@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=lee@kernel.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mschmidt@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=vadim.fedorenko@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox