[PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe

Ivan Vecera posted 14 patches 4 months ago
There is a newer version of this series
[PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe
Posted by Ivan Vecera 4 months ago
Several configuration parameters will remain constant at runtime,
so we can load them during probe to avoid excessive reads from
the hardware.

Read the following parameters from the device during probe and store
them for later use:

* enablement status and frequencies of the synthesizers and their
  associated DPLL channels
* enablement status and type (single-ended or differential) of input pins
* associated synthesizers, signal format, and enablement status of
  outputs

Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
 drivers/dpll/zl3073x/core.c | 248 +++++++++++++++++++++++++++++++
 drivers/dpll/zl3073x/core.h | 286 ++++++++++++++++++++++++++++++++++++
 drivers/dpll/zl3073x/regs.h |  65 ++++++++
 3 files changed, 599 insertions(+)

diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
index 60344761545d8..3a57c85f902c4 100644
--- a/drivers/dpll/zl3073x/core.c
+++ b/drivers/dpll/zl3073x/core.c
@@ -6,6 +6,7 @@
 #include <linux/dev_printk.h>
 #include <linux/device.h>
 #include <linux/export.h>
+#include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/netlink.h>
 #include <linux/regmap.h>
@@ -376,6 +377,25 @@ int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask)
 					ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US);
 }
 
+int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
+		  unsigned int mask_reg, u16 mask_val)
+{
+	int rc;
+
+	/* Set mask for the operation */
+	rc = zl3073x_write_u16(zldev, mask_reg, mask_val);
+	if (rc)
+		return rc;
+
+	/* Trigger the operation */
+	rc = zl3073x_write_u8(zldev, op_reg, op_val);
+	if (rc)
+		return rc;
+
+	/* Wait for the operation to actually finish */
+	return zl3073x_poll_zero_u8(zldev, op_reg, op_val);
+}
+
 /**
  * zl3073x_devlink_info_get - Devlink device info callback
  * @devlink: devlink structure pointer
@@ -484,6 +504,229 @@ struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev)
 }
 EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X");
 
+/**
+ * zl3073x_ref_state_fetch - get input reference state
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: input reference index to fetch state for
+ *
+ * Function fetches information for the given input reference that are
+ * invariant and stores them for later use.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int
+zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+	struct zl3073x_ref *input = &zldev->ref[index];
+	u8 ref_config;
+	int rc;
+
+	/* If the input is differential then the configuration for N-pin
+	 * reference is ignored and P-pin config is used for both.
+	 */
+	if (zl3073x_is_n_pin(index) &&
+	    zl3073x_ref_is_diff(zldev, index - 1)) {
+		input->enabled = zl3073x_ref_is_enabled(zldev, index - 1);
+		input->diff = true;
+
+		return 0;
+	}
+
+	guard(mutex)(&zldev->multiop_lock);
+
+	/* Read reference configuration */
+	rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
+			   ZL_REG_REF_MB_MASK, BIT(index));
+	if (rc)
+		return rc;
+
+	/* Read ref_config register */
+	rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config);
+	if (rc)
+		return rc;
+
+	input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config);
+	input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config);
+
+	dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
+		input->enabled ? "enabled" : "disabled",
+		input->diff ? "differential" : "single-ended");
+
+	return rc;
+}
+
+/**
+ * zl3073x_out_state_fetch - get output state
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: output index to fetch state for
+ *
+ * Function fetches information for the given output (not output pin)
+ * that are invariant and stores them for later use.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int
+zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+	struct zl3073x_out *out = &zldev->out[index];
+	u8 output_ctrl, output_mode;
+	int rc;
+
+	/* Read output configuration */
+	rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl);
+	if (rc)
+		return rc;
+
+	/* Store info about output enablement and synthesizer the output
+	 * is connected to.
+	 */
+	out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl);
+	out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl);
+
+	dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
+		out->enabled ? "enabled" : "disabled", out->synth);
+
+	guard(mutex)(&zldev->multiop_lock);
+
+	/* Read output configuration */
+	rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
+			   ZL_REG_OUTPUT_MB_MASK, BIT(index));
+	if (rc)
+		return rc;
+
+	/* Read output_mode */
+	rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
+	if (rc)
+		return rc;
+
+	/* Extract and store output signal format */
+	out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT,
+				       output_mode);
+
+	dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
+		out->signal_format);
+
+	return rc;
+}
+
+/**
+ * zl3073x_synth_state_fetch - get synth state
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: synth index to fetch state for
+ *
+ * Function fetches information for the given synthesizer that are
+ * invariant and stores them for later use.
+ *
+ * Return: 0 on success, <0 on error
+ */
+static int
+zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+	struct zl3073x_synth *synth = &zldev->synth[index];
+	u16 base, m, n;
+	u8 synth_ctrl;
+	u32 mult;
+	int rc;
+
+	/* Read synth control register */
+	rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl);
+	if (rc)
+		return rc;
+
+	/* Store info about synth enablement and DPLL channel the synth is
+	 * driven by.
+	 */
+	synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl);
+	synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl);
+
+	dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index,
+		synth->enabled ? "enabled" : "disabled", synth->dpll);
+
+	guard(mutex)(&zldev->multiop_lock);
+
+	/* Read synth configuration */
+	rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
+			   ZL_REG_SYNTH_MB_MASK, BIT(index));
+	if (rc)
+		return rc;
+
+	/* The output frequency is determined by the following formula:
+	 * base * multiplier * numerator / denominator
+	 *
+	 * Read registers with these values
+	 */
+	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base);
+	if (rc)
+		return rc;
+
+	rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult);
+	if (rc)
+		return rc;
+
+	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m);
+	if (rc)
+		return rc;
+
+	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n);
+	if (rc)
+		return rc;
+
+	/* Check denominator for zero to avoid div by 0 */
+	if (!n) {
+		dev_err(zldev->dev,
+			"Zero divisor for SYNTH%u retrieved from device\n",
+			index);
+		return -EINVAL;
+	}
+
+	/* Compute and store synth frequency */
+	zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n);
+
+	dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
+		zldev->synth[index].freq);
+
+	return rc;
+}
+
+static int
+zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
+{
+	int rc;
+	u8 i;
+
+	for (i = 0; i < ZL3073X_NUM_REFS; i++) {
+		rc = zl3073x_ref_state_fetch(zldev, i);
+		if (rc) {
+			dev_err(zldev->dev,
+				"Failed to fetch input state: %pe\n",
+				ERR_PTR(rc));
+			return rc;
+		}
+	}
+
+	for (i = 0; i < ZL3073X_NUM_SYNTHS; i++) {
+		rc = zl3073x_synth_state_fetch(zldev, i);
+		if (rc) {
+			dev_err(zldev->dev,
+				"Failed to fetch synth state: %pe\n",
+				ERR_PTR(rc));
+			return rc;
+		}
+	}
+
+	for (i = 0; i < ZL3073X_NUM_OUTS; i++) {
+		rc = zl3073x_out_state_fetch(zldev, i);
+		if (rc) {
+			dev_err(zldev->dev,
+				"Failed to fetch output state: %pe\n",
+				ERR_PTR(rc));
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
 static void zl3073x_devlink_unregister(void *ptr)
 {
 	devlink_unregister(ptr);
@@ -551,6 +794,11 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
 		return dev_err_probe(zldev->dev, rc,
 				     "Failed to initialize mutex\n");
 
+	/* Fetch device state */
+	rc = zl3073x_dev_state_fetch(zldev);
+	if (rc)
+		return rc;
+
 	/* Register the device as devlink device */
 	devlink = priv_to_devlink(zldev);
 	devlink_register(devlink);
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
index 1a77a69f85a26..0d052c02065e5 100644
--- a/drivers/dpll/zl3073x/core.h
+++ b/drivers/dpll/zl3073x/core.h
@@ -6,19 +6,70 @@
 #include <linux/mutex.h>
 #include <linux/types.h>
 
+#include "regs.h"
+
 struct device;
 struct regmap;
 
+/*
+ * Hardware limits for ZL3073x chip family
+ */
+#define ZL3073X_NUM_REFS	10
+#define ZL3073X_NUM_OUTS	10
+#define ZL3073X_NUM_SYNTHS	5
+
+/**
+ * struct zl3073x_ref - input reference invariant info
+ * @enabled: input reference is enabled or disabled
+ * @diff: true if input reference is differential
+ */
+struct zl3073x_ref {
+	bool	enabled;
+	bool	diff;
+};
+
+/**
+ * struct zl3073x_out - output invariant info
+ * @enabled: out is enabled or disabled
+ * @synth: synthesizer the out is connected to
+ * @signal_format: out signal format
+ */
+struct zl3073x_out {
+	bool	enabled;
+	u8	synth;
+	u8	signal_format;
+};
+
+/**
+ * struct zl3073x_synth - synthesizer invariant info
+ * @freq: synthesizer frequency
+ * @dpll: ID of DPLL the synthesizer is driven by
+ * @enabled: synth is enabled or disabled
+ */
+struct zl3073x_synth {
+	u32	freq;
+	u8	dpll;
+	bool	enabled;
+};
+
 /**
  * struct zl3073x_dev - zl3073x device
  * @dev: pointer to device
  * @regmap: regmap to access device registers
  * @multiop_lock: to serialize multiple register operations
+ * @ref: array of input references' invariants
+ * @out: array of outs' invariants
+ * @synth: array of synths' invariants
  */
 struct zl3073x_dev {
 	struct device		*dev;
 	struct regmap		*regmap;
 	struct mutex		multiop_lock;
+
+	/* Invariants */
+	struct zl3073x_ref	ref[ZL3073X_NUM_REFS];
+	struct zl3073x_out	out[ZL3073X_NUM_OUTS];
+	struct zl3073x_synth	synth[ZL3073X_NUM_SYNTHS];
 };
 
 enum zl3073x_chip_type {
@@ -46,6 +97,8 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
  * Registers operations
  **********************/
 
+int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
+		  unsigned int mask_reg, u16 mask_val);
 int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask);
 int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val);
 int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val);
@@ -56,4 +109,237 @@ int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val);
 int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val);
 int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val);
 
+static inline bool
+zl3073x_is_n_pin(u8 id)
+{
+	/* P-pins ids are even while N-pins are odd */
+	return id & 1;
+}
+
+static inline bool
+zl3073x_is_p_pin(u8 id)
+{
+	return !zl3073x_is_n_pin(id);
+}
+
+/**
+ * zl3073x_input_pin_ref_get - get reference for given input pin
+ * @id: input pin id
+ *
+ * Return: reference id for the given input pin
+ */
+static inline u8
+zl3073x_input_pin_ref_get(u8 id)
+{
+	return id;
+}
+
+/**
+ * zl3073x_output_pin_out_get - get output for the given output pin
+ * @id: output pin id
+ *
+ * Return: output id for the given output pin
+ */
+static inline u8
+zl3073x_output_pin_out_get(u8 id)
+{
+	/* Output pin pair shares the single output */
+	return id / 2;
+}
+
+/**
+ * zl3073x_ref_is_diff - check if the given input reference is differential
+ * @zldev: pointer to zl3073x device
+ * @index: input reference index
+ *
+ * Return: true if reference is differential, false if reference is single-ended
+ */
+static inline bool
+zl3073x_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
+{
+	return zldev->ref[index].diff;
+}
+
+/**
+ * zl3073x_ref_is_enabled - check if the given input reference is enabled
+ * @zldev: pointer to zl3073x device
+ * @index: input reference index
+ *
+ * Return: true if input refernce is enabled, false otherwise
+ */
+static inline bool
+zl3073x_ref_is_enabled(struct zl3073x_dev *zldev, u8 index)
+{
+	return zldev->ref[index].enabled;
+}
+
+/**
+ * zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
+ * @zldev: pointer to zl3073x device
+ * @index: synth index
+ *
+ * Return: ID of DPLL the given synthetizer is driven by
+ */
+static inline u8
+zl3073x_synth_dpll_get(struct zl3073x_dev *zldev, u8 index)
+{
+	return zldev->synth[index].dpll;
+}
+
+/**
+ * zl3073x_synth_freq_get - get synth current freq
+ * @zldev: pointer to zl3073x device
+ * @index: synth index
+ *
+ * Return: frequency of given synthetizer
+ */
+static inline u32
+zl3073x_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
+{
+	return zldev->synth[index].freq;
+}
+
+/**
+ * zl3073x_synth_is_enabled - check if the given synth is enabled
+ * @zldev: pointer to zl3073x device
+ * @index: synth index
+ *
+ * Return: true if synth is enabled, false otherwise
+ */
+static inline bool
+zl3073x_synth_is_enabled(struct zl3073x_dev *zldev, u8 index)
+{
+	return zldev->synth[index].enabled;
+}
+
+/**
+ * zl3073x_out_synth_get - get synth connected to given output
+ * @zldev: pointer to zl3073x device
+ * @index: output index
+ *
+ * Return: index of synth connected to given output.
+ */
+static inline u8
+zl3073x_out_synth_get(struct zl3073x_dev *zldev, u8 index)
+{
+	return zldev->out[index].synth;
+}
+
+/**
+ * zl3073x_out_is_enabled - check if the given output is enabled
+ * @zldev: pointer to zl3073x device
+ * @index: output index
+ *
+ * Return: true if the output is enabled, false otherwise
+ */
+static inline bool
+zl3073x_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
+{
+	u8 synth;
+
+	/* Output is enabled only if associated synth is enabled */
+	synth = zl3073x_out_synth_get(zldev, index);
+	if (zl3073x_synth_is_enabled(zldev, synth))
+		return zldev->out[index].enabled;
+
+	return false;
+}
+
+/**
+ * zl3073x_out_signal_format_get - get output signal format
+ * @zldev: pointer to zl3073x device
+ * @index: output index
+ *
+ * Return: signal format of given output
+ */
+static inline u8
+zl3073x_out_signal_format_get(struct zl3073x_dev *zldev, u8 index)
+{
+	return zldev->out[index].signal_format;
+}
+
+/**
+ * zl3073x_out_dpll_get - get DPLL ID the output is driven by
+ * @zldev: pointer to zl3073x device
+ * @index: output index
+ *
+ * Return: ID of DPLL the given output is driven by
+ */
+static inline
+u8 zl3073x_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
+{
+	u8 synth;
+
+	/* Get synthesizer connected to given output */
+	synth = zl3073x_out_synth_get(zldev, index);
+
+	/* Return DPLL that drives the synth */
+	return zl3073x_synth_dpll_get(zldev, synth);
+}
+
+/**
+ * zl3073x_out_is_diff - check if the given output is differential
+ * @zldev: pointer to zl3073x device
+ * @index: output index
+ *
+ * Return: true if output is differential, false if output is single-ended
+ */
+static inline bool
+zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index)
+{
+	switch (zl3073x_out_signal_format_get(zldev, index)) {
+	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
+	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF:
+	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
+		return true;
+	default:
+		break;
+	}
+
+	return false;
+}
+
+/**
+ * zl3073x_output_pin_is_enabled - check if the given output pin is enabled
+ * @zldev: pointer to zl3073x device
+ * @id: output pin id
+ *
+ * Checks if the output of the given output pin is enabled and also that
+ * its signal format also enables the given pin.
+ *
+ * Return: true if output pin is enabled, false if output pin is disabled
+ */
+static inline bool
+zl3073x_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
+{
+	u8 output = zl3073x_output_pin_out_get(id);
+
+	/* Check if the whole output is enabled */
+	if (!zl3073x_out_is_enabled(zldev, output))
+		return false;
+
+	/* Check signal format */
+	switch (zl3073x_out_signal_format_get(zldev, output)) {
+	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED:
+		/* Both output pins are disabled by signal format */
+		return false;
+
+	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P:
+		/* Output is one single ended P-pin output */
+		if (zl3073x_is_n_pin(id))
+			return false;
+		break;
+	case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N:
+		/* Output is one single ended N-pin output */
+		if (zl3073x_is_p_pin(id))
+			return false;
+		break;
+	default:
+		/* For other format both pins are enabled */
+		break;
+	}
+
+	return true;
+}
+
 #endif /* _ZL3073X_H */
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h
index 08bf595935ea1..753b42d8b2093 100644
--- a/drivers/dpll/zl3073x/regs.h
+++ b/drivers/dpll/zl3073x/regs.h
@@ -72,4 +72,69 @@
 #define ZL_REG_FW_VER				ZL_REG(0, 0x05, 2)
 #define ZL_REG_CUSTOM_CONFIG_VER		ZL_REG(0, 0x07, 4)
 
+/***********************************
+ * Register Page 9, Synth and Output
+ ***********************************/
+
+#define ZL_REG_SYNTH_CTRL(_idx)						\
+	ZL_REG_IDX(_idx, 9, 0x00, 1, ZL3073X_NUM_SYNTHS, 1)
+#define ZL_SYNTH_CTRL_EN			BIT(0)
+#define ZL_SYNTH_CTRL_DPLL_SEL			GENMASK(6, 4)
+
+#define ZL_REG_OUTPUT_CTRL(_idx)					\
+	ZL_REG_IDX(_idx, 9, 0x28, 1, ZL3073X_NUM_OUTS, 1)
+#define ZL_OUTPUT_CTRL_EN			BIT(0)
+#define ZL_OUTPUT_CTRL_SYNTH_SEL		GENMASK(6, 4)
+
+/*******************************
+ * Register Page 10, Ref Mailbox
+ *******************************/
+
+#define ZL_REG_REF_MB_MASK			ZL_REG(10, 0x02, 2)
+
+#define ZL_REG_REF_MB_SEM			ZL_REG(10, 0x04, 1)
+#define ZL_REF_MB_SEM_WR			BIT(0)
+#define ZL_REF_MB_SEM_RD			BIT(1)
+
+#define ZL_REG_REF_CONFIG			ZL_REG(10, 0x0d, 1)
+#define ZL_REF_CONFIG_ENABLE			BIT(0)
+#define ZL_REF_CONFIG_DIFF_EN			BIT(2)
+
+/*********************************
+ * Register Page 13, Synth Mailbox
+ *********************************/
+
+#define ZL_REG_SYNTH_MB_MASK			ZL_REG(13, 0x02, 2)
+
+#define ZL_REG_SYNTH_MB_SEM			ZL_REG(13, 0x04, 1)
+#define ZL_SYNTH_MB_SEM_WR			BIT(0)
+#define ZL_SYNTH_MB_SEM_RD			BIT(1)
+
+#define ZL_REG_SYNTH_FREQ_BASE			ZL_REG(13, 0x06, 2)
+#define ZL_REG_SYNTH_FREQ_MULT			ZL_REG(13, 0x08, 4)
+#define ZL_REG_SYNTH_FREQ_M			ZL_REG(13, 0x0c, 2)
+#define ZL_REG_SYNTH_FREQ_N			ZL_REG(13, 0x0e, 2)
+
+/**********************************
+ * Register Page 14, Output Mailbox
+ **********************************/
+#define ZL_REG_OUTPUT_MB_MASK			ZL_REG(14, 0x02, 2)
+
+#define ZL_REG_OUTPUT_MB_SEM			ZL_REG(14, 0x04, 1)
+#define ZL_OUTPUT_MB_SEM_WR			BIT(0)
+#define ZL_OUTPUT_MB_SEM_RD			BIT(1)
+
+#define ZL_REG_OUTPUT_MODE			ZL_REG(14, 0x05, 1)
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT		GENMASK(7, 4)
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED	0
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS	1
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF	2
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM	3
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2		4
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P		5
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N		6
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_INV	7
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV	12
+#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV	15
+
 #endif /* _ZL3073X_REGS_H */
-- 
2.49.0
Re: [PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe
Posted by kernel test robot 3 months, 4 weeks ago
Hi Ivan,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Ivan-Vecera/dt-bindings-dpll-Add-DPLL-device-and-pin/20250613-041005
base:   net-next/main
patch link:    https://lore.kernel.org/r/20250612200145.774195-7-ivecera%40redhat.com
patch subject: [PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe
config: alpha-randconfig-r061-20250614 (https://download.01.org/0day-ci/archive/20250614/202506140541.KcP4ErN5-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 10.5.0

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506140541.KcP4ErN5-lkp@intel.com/

cocci warnings: (new ones prefixed by >>)
>> drivers/dpll/zl3073x/core.c:552:2-16: opportunity for str_enabled_disabled(input -> enabled)
>> drivers/dpll/zl3073x/core.c:587:2-14: opportunity for str_enabled_disabled(out -> enabled)
>> drivers/dpll/zl3073x/core.c:643:2-16: opportunity for str_enabled_disabled(synth -> enabled)

vim +552 drivers/dpll/zl3073x/core.c

   506	
   507	/**
   508	 * zl3073x_ref_state_fetch - get input reference state
   509	 * @zldev: pointer to zl3073x_dev structure
   510	 * @index: input reference index to fetch state for
   511	 *
   512	 * Function fetches information for the given input reference that are
   513	 * invariant and stores them for later use.
   514	 *
   515	 * Return: 0 on success, <0 on error
   516	 */
   517	static int
   518	zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
   519	{
   520		struct zl3073x_ref *input = &zldev->ref[index];
   521		u8 ref_config;
   522		int rc;
   523	
   524		/* If the input is differential then the configuration for N-pin
   525		 * reference is ignored and P-pin config is used for both.
   526		 */
   527		if (zl3073x_is_n_pin(index) &&
   528		    zl3073x_ref_is_diff(zldev, index - 1)) {
   529			input->enabled = zl3073x_ref_is_enabled(zldev, index - 1);
   530			input->diff = true;
   531	
   532			return 0;
   533		}
   534	
   535		guard(mutex)(&zldev->multiop_lock);
   536	
   537		/* Read reference configuration */
   538		rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
   539				   ZL_REG_REF_MB_MASK, BIT(index));
   540		if (rc)
   541			return rc;
   542	
   543		/* Read ref_config register */
   544		rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config);
   545		if (rc)
   546			return rc;
   547	
   548		input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config);
   549		input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config);
   550	
   551		dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
 > 552			input->enabled ? "enabled" : "disabled",
   553			input->diff ? "differential" : "single-ended");
   554	
   555		return rc;
   556	}
   557	
   558	/**
   559	 * zl3073x_out_state_fetch - get output state
   560	 * @zldev: pointer to zl3073x_dev structure
   561	 * @index: output index to fetch state for
   562	 *
   563	 * Function fetches information for the given output (not output pin)
   564	 * that are invariant and stores them for later use.
   565	 *
   566	 * Return: 0 on success, <0 on error
   567	 */
   568	static int
   569	zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
   570	{
   571		struct zl3073x_out *out = &zldev->out[index];
   572		u8 output_ctrl, output_mode;
   573		int rc;
   574	
   575		/* Read output configuration */
   576		rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl);
   577		if (rc)
   578			return rc;
   579	
   580		/* Store info about output enablement and synthesizer the output
   581		 * is connected to.
   582		 */
   583		out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl);
   584		out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl);
   585	
   586		dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
 > 587			out->enabled ? "enabled" : "disabled", out->synth);
   588	
   589		guard(mutex)(&zldev->multiop_lock);
   590	
   591		/* Read output configuration */
   592		rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
   593				   ZL_REG_OUTPUT_MB_MASK, BIT(index));
   594		if (rc)
   595			return rc;
   596	
   597		/* Read output_mode */
   598		rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
   599		if (rc)
   600			return rc;
   601	
   602		/* Extract and store output signal format */
   603		out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT,
   604					       output_mode);
   605	
   606		dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
   607			out->signal_format);
   608	
   609		return rc;
   610	}
   611	
   612	/**
   613	 * zl3073x_synth_state_fetch - get synth state
   614	 * @zldev: pointer to zl3073x_dev structure
   615	 * @index: synth index to fetch state for
   616	 *
   617	 * Function fetches information for the given synthesizer that are
   618	 * invariant and stores them for later use.
   619	 *
   620	 * Return: 0 on success, <0 on error
   621	 */
   622	static int
   623	zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
   624	{
   625		struct zl3073x_synth *synth = &zldev->synth[index];
   626		u16 base, m, n;
   627		u8 synth_ctrl;
   628		u32 mult;
   629		int rc;
   630	
   631		/* Read synth control register */
   632		rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl);
   633		if (rc)
   634			return rc;
   635	
   636		/* Store info about synth enablement and DPLL channel the synth is
   637		 * driven by.
   638		 */
   639		synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl);
   640		synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl);
   641	
   642		dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index,
 > 643			synth->enabled ? "enabled" : "disabled", synth->dpll);
   644	
   645		guard(mutex)(&zldev->multiop_lock);
   646	
   647		/* Read synth configuration */
   648		rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
   649				   ZL_REG_SYNTH_MB_MASK, BIT(index));
   650		if (rc)
   651			return rc;
   652	
   653		/* The output frequency is determined by the following formula:
   654		 * base * multiplier * numerator / denominator
   655		 *
   656		 * Read registers with these values
   657		 */
   658		rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base);
   659		if (rc)
   660			return rc;
   661	
   662		rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult);
   663		if (rc)
   664			return rc;
   665	
   666		rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m);
   667		if (rc)
   668			return rc;
   669	
   670		rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n);
   671		if (rc)
   672			return rc;
   673	
   674		/* Check denominator for zero to avoid div by 0 */
   675		if (!n) {
   676			dev_err(zldev->dev,
   677				"Zero divisor for SYNTH%u retrieved from device\n",
   678				index);
   679			return -EINVAL;
   680		}
   681	
   682		/* Compute and store synth frequency */
   683		zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n);
   684	
   685		dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
   686			zldev->synth[index].freq);
   687	
   688		return rc;
   689	}
   690	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe
Posted by Ivan Vecera 3 months, 4 weeks ago

On 13. 06. 25 11:46 odp., kernel test robot wrote:
> Hi Ivan,
> 
> kernel test robot noticed the following build warnings:
> 
> [auto build test WARNING on net-next/main]
> 
> url:https://github.com/intel-lab-lkp/linux/commits/Ivan-Vecera/dt-bindings- 
> dpll-Add-DPLL-device-and-pin/20250613-041005
> base:   net-next/main
> patch link:https://lore.kernel.org/r/20250612200145.774195-7-ivecera%40redhat.com
> patch subject: [PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe
> config: alpha-randconfig-r061-20250614 (https://download.01.org/0day-ci/archive/20250614/202506140541.KcP4ErN5- 
> lkp@intel.com/config)
> compiler: alpha-linux-gcc (GCC) 10.5.0
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot<lkp@intel.com>
> | Closes:https://lore.kernel.org/oe-kbuild-all/202506140541.KcP4ErN5-lkp@intel.com/

Jakub, Dave, Paolo,
should I fix this in v10 or in a follow-up in the part 2?

Thanks,
Ivan
Re: [PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe
Posted by Jakub Kicinski 3 months, 4 weeks ago
On Sat, 14 Jun 2025 12:39:08 +0200 Ivan Vecera wrote:
> should I fix this in v10 or in a follow-up in the part 2?

I don't love this but yes, please fix and respin. We auto-discard
series that get a build bot reply.
Re: [PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe
Posted by Vadim Fedorenko 3 months, 4 weeks ago
On 12/06/2025 21:01, Ivan Vecera wrote:
> Several configuration parameters will remain constant at runtime,
> so we can load them during probe to avoid excessive reads from
> the hardware.
> 
> Read the following parameters from the device during probe and store
> them for later use:
> 
> * enablement status and frequencies of the synthesizers and their
>    associated DPLL channels
> * enablement status and type (single-ended or differential) of input pins
> * associated synthesizers, signal format, and enablement status of
>    outputs
> 
> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
> ---
>   drivers/dpll/zl3073x/core.c | 248 +++++++++++++++++++++++++++++++
>   drivers/dpll/zl3073x/core.h | 286 ++++++++++++++++++++++++++++++++++++
>   drivers/dpll/zl3073x/regs.h |  65 ++++++++
>   3 files changed, 599 insertions(+)
> 
> diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
> index 60344761545d8..3a57c85f902c4 100644
> --- a/drivers/dpll/zl3073x/core.c
> +++ b/drivers/dpll/zl3073x/core.c
> @@ -6,6 +6,7 @@
>   #include <linux/dev_printk.h>
>   #include <linux/device.h>
>   #include <linux/export.h>
> +#include <linux/math64.h>
>   #include <linux/module.h>
>   #include <linux/netlink.h>
>   #include <linux/regmap.h>
> @@ -376,6 +377,25 @@ int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask)
>   					ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US);
>   }
>   
> +int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
> +		  unsigned int mask_reg, u16 mask_val)
> +{
> +	int rc;
> +
> +	/* Set mask for the operation */
> +	rc = zl3073x_write_u16(zldev, mask_reg, mask_val);
> +	if (rc)
> +		return rc;
> +
> +	/* Trigger the operation */
> +	rc = zl3073x_write_u8(zldev, op_reg, op_val);
> +	if (rc)
> +		return rc;
> +
> +	/* Wait for the operation to actually finish */
> +	return zl3073x_poll_zero_u8(zldev, op_reg, op_val);
> +}
> +
>   /**
>    * zl3073x_devlink_info_get - Devlink device info callback
>    * @devlink: devlink structure pointer
> @@ -484,6 +504,229 @@ struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev)
>   }
>   EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X");
>   
> +/**
> + * zl3073x_ref_state_fetch - get input reference state
> + * @zldev: pointer to zl3073x_dev structure
> + * @index: input reference index to fetch state for
> + *
> + * Function fetches information for the given input reference that are
> + * invariant and stores them for later use.
> + *
> + * Return: 0 on success, <0 on error
> + */
> +static int
> +zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
> +{
> +	struct zl3073x_ref *input = &zldev->ref[index];
> +	u8 ref_config;
> +	int rc;
> +
> +	/* If the input is differential then the configuration for N-pin
> +	 * reference is ignored and P-pin config is used for both.
> +	 */
> +	if (zl3073x_is_n_pin(index) &&
> +	    zl3073x_ref_is_diff(zldev, index - 1)) {
> +		input->enabled = zl3073x_ref_is_enabled(zldev, index - 1);
> +		input->diff = true;
> +
> +		return 0;
> +	}
> +
> +	guard(mutex)(&zldev->multiop_lock);
> +
> +	/* Read reference configuration */
> +	rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
> +			   ZL_REG_REF_MB_MASK, BIT(index));
> +	if (rc)
> +		return rc;
> +
> +	/* Read ref_config register */
> +	rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config);
> +	if (rc)
> +		return rc;
> +
> +	input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config);
> +	input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config);
> +
> +	dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
> +		input->enabled ? "enabled" : "disabled",
> +		input->diff ? "differential" : "single-ended");
> +
> +	return rc;
> +}
> +
> +/**
> + * zl3073x_out_state_fetch - get output state
> + * @zldev: pointer to zl3073x_dev structure
> + * @index: output index to fetch state for
> + *
> + * Function fetches information for the given output (not output pin)
> + * that are invariant and stores them for later use.
> + *
> + * Return: 0 on success, <0 on error
> + */
> +static int
> +zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
> +{
> +	struct zl3073x_out *out = &zldev->out[index];
> +	u8 output_ctrl, output_mode;
> +	int rc;
> +
> +	/* Read output configuration */
> +	rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl);
> +	if (rc)
> +		return rc;
> +
> +	/* Store info about output enablement and synthesizer the output
> +	 * is connected to.
> +	 */
> +	out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl);
> +	out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl);
> +
> +	dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
> +		out->enabled ? "enabled" : "disabled", out->synth);
> +
> +	guard(mutex)(&zldev->multiop_lock);
> +
> +	/* Read output configuration */
> +	rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
> +			   ZL_REG_OUTPUT_MB_MASK, BIT(index));
> +	if (rc)
> +		return rc;
> +
> +	/* Read output_mode */
> +	rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
> +	if (rc)
> +		return rc;
> +
> +	/* Extract and store output signal format */
> +	out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT,
> +				       output_mode);
> +
> +	dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
> +		out->signal_format);
> +
> +	return rc;
> +}
> +
> +/**
> + * zl3073x_synth_state_fetch - get synth state
> + * @zldev: pointer to zl3073x_dev structure
> + * @index: synth index to fetch state for
> + *
> + * Function fetches information for the given synthesizer that are
> + * invariant and stores them for later use.
> + *
> + * Return: 0 on success, <0 on error
> + */
> +static int
> +zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
> +{
> +	struct zl3073x_synth *synth = &zldev->synth[index];
> +	u16 base, m, n;
> +	u8 synth_ctrl;
> +	u32 mult;
> +	int rc;
> +
> +	/* Read synth control register */
> +	rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl);
> +	if (rc)
> +		return rc;
> +
> +	/* Store info about synth enablement and DPLL channel the synth is
> +	 * driven by.
> +	 */
> +	synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl);
> +	synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl);
> +
> +	dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index,
> +		synth->enabled ? "enabled" : "disabled", synth->dpll);
> +
> +	guard(mutex)(&zldev->multiop_lock);

Not a strong suggestion, but it would be good to follow netdev style
(same for some previous functions):

https://docs.kernel.org/process/maintainer-netdev.html#using-device-managed-and-cleanup-h-constructs

"Use of guard() is discouraged within any function longer than 20 lines,
scoped_guard() is considered more readable. Using normal lock/unlock is 
still (weakly) preferred."

> +
> +	/* Read synth configuration */
> +	rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
> +			   ZL_REG_SYNTH_MB_MASK, BIT(index));
> +	if (rc)
> +		return rc;
> +
> +	/* The output frequency is determined by the following formula:
> +	 * base * multiplier * numerator / denominator
> +	 *
> +	 * Read registers with these values
> +	 */
> +	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base);
> +	if (rc)
> +		return rc;
> +
> +	rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult);
> +	if (rc)
> +		return rc;
> +
> +	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m);
> +	if (rc)
> +		return rc;
> +
> +	rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n);
> +	if (rc)
> +		return rc;
> +
> +	/* Check denominator for zero to avoid div by 0 */
> +	if (!n) {
> +		dev_err(zldev->dev,
> +			"Zero divisor for SYNTH%u retrieved from device\n",
> +			index);
> +		return -EINVAL;
> +	}
> +
> +	/* Compute and store synth frequency */
> +	zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n);
> +
> +	dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
> +		zldev->synth[index].freq);
> +
> +	return rc;
> +}
> +
[...]
Re: [PATCH net-next v9 06/14] dpll: zl3073x: Fetch invariants during probe
Posted by Ivan Vecera 3 months, 4 weeks ago

On 13. 06. 25 9:13 odp., Vadim Fedorenko wrote:
>> +    synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl);
>> +    synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl);
>> +
>> +    dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index,
>> +        synth->enabled ? "enabled" : "disabled", synth->dpll);
>> +
>> +    guard(mutex)(&zldev->multiop_lock);
> 
> Not a strong suggestion, but it would be good to follow netdev style
> (same for some previous functions):

Hi Vadim,

I'm using guard() on places (functions) where it is necessary to hold
the lock from that place to the end of the function. Due to this
scoped_guard() does not give any advantage. Using classic mutex_lock()
and mutex_unlock() would only increases the risks of locking-related
bugs. Also manual locking enforces to use mutex_unlock() or goto in
all error paths after taking lock.

> https://docs.kernel.org/process/maintainer-netdev.html#using-device- 
> managed-and-cleanup-h-constructs
> 
> "Use of guard() is discouraged within any function longer than 20 lines,
> scoped_guard() is considered more readable. Using normal lock/unlock is 
> still (weakly) preferred."
> 
>> +
>> +    /* Read synth configuration */
>> +    rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
>> +               ZL_REG_SYNTH_MB_MASK, BIT(index));
>> +    if (rc)
>> +        return rc;
>> +
>> +    /* The output frequency is determined by the following formula:
>> +     * base * multiplier * numerator / denominator
>> +     *
>> +     * Read registers with these values
>> +     */
>> +    rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base);
>> +    if (rc)
>> +        return rc;
>> +
>> +    rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult);
>> +    if (rc)
>> +        return rc;
>> +
>> +    rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m);
>> +    if (rc)
>> +        return rc;
>> +
>> +    rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n);
>> +    if (rc)
>> +        return rc;
>> +

---> You have to keep the lock to here.

>> +    /* Check denominator for zero to avoid div by 0 */
>> +    if (!n) {
>> +        dev_err(zldev->dev,
>> +            "Zero divisor for SYNTH%u retrieved from device\n",
>> +            index);
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Compute and store synth frequency */
>> +    zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n);
>> +
>> +    dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
>> +        zldev->synth[index].freq);
>> +
>> +    return rc;
>> +} 

This kind of function (above) is mailbox-read:
1. Take lock
2. Ask firmware to fill mailbox latch registers
3. Read latch1
4. ...
5. Unlock

But in later commits there are mailbox-write functions that:
1. Take lock
2. Ask firmware to fill mailbox latch registers
3. Write or read-update-write latch registers
4. ...
5. Ask firmware to update HW from the latch registers (commit)
6. Unlock

Step 5 here is usually represented by:

return zl3073x_mb_op(zldev, ZL_REG_*_MB_SEM, ZL_*_MB_SEM_RD,
                      ZL_REG_*_MB_MASK, BIT(index));

and here is an advantage of guard() that unlocks the mutex automatically
after zl3073x_mb_op() and prior returning its return value.

Thanks,
Ivan