[PATCH 08/28] mfd: zl3073x: Add functions to work with register mailboxes

Ivan Vecera
2025-04-07T17:29:41Z

Registers present in page 10 and higher are organized in so-called
register mailboxes. These mailboxes are used to read and write
configuration of particular object (dpll, output, reference & synth).

Each of these register pages contains mask register that is used by
the driver to indicate an index of requested object to work with and
also semaphore register to indicate what operation is requested.
The rest of registers in the particular register page are latch
registers that are filled by the firmware during read operation or by
the driver prior write operation.

For read operation the driver...
1) ... updates the mailbox mask register with index of particular object
2) ... sets the mailbox semaphore register read bit
3) ... waits for the semaphore register read bit to be cleared by FW
4) ... reads the configuration from latch registers

For write operation the driver...
1) ... writes the requested configuration to latch registers
2) ... sets the mailbox mask register for the DPLL to be updated
3) ... sets the mailbox semaphore register bit for the write operation
4) ... waits for the semaphore register bit to be cleared by FW

Add functions to read and write mailboxes for all supported object types.

Reviewed-by: Michal Schmidt <mschmidt@redhat.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
drivers/mfd/zl3073x-core.c | 185 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/zl3073x.h | 12 +++
2 files changed, 197 insertions(+)

diff --git a/drivers/mfd/zl3073x-core.c b/drivers/mfd/zl3073x-core.c
index 33e76666e5694..feb139b550571 100644
--- a/drivers/mfd/zl3073x-core.c
+++ b/drivers/mfd/zl3073x-core.c
@@ -14,6 +14,47 @@ ZL3073X_REG16_DEF(revision, 0x0003);
ZL3073X_REG16_DEF(fw_ver, 0x0005);
ZL3073X_REG32_DEF(custom_config_ver, 0x0007);

+/*
+ * Register Map Page 10, Ref Mailbox
+ */
+ZL3073X_REG16_DEF(ref_mb_mask, 0x502);
+#define REF_MB_MASK GENMASK(9, 0)
+
+ZL3073X_REG8_DEF(ref_mb_sem, 0x504);
+#define REF_MB_SEM_WR BIT(0)
+#define REF_MB_SEM_RD BIT(1)
+
+/*
+ * Register Map Page 12, DPLL Mailbox
+ */
+ZL3073X_REG16_DEF(dpll_mb_mask, 0x602);
+
+ZL3073X_REG8_DEF(dpll_mb_sem, 0x604);
+#define DPLL_MB_SEM_WR BIT(0)
+#define DPLL_MB_SEM_RD BIT(1)
+
+/*
+ * Register Map Page 13, Synth Mailbox
+ */
+ZL3073X_REG16_DEF(synth_mb_mask, 0x682);
+
+ZL3073X_REG8_DEF(synth_mb_sem, 0x684);
+#define SYNTH_MB_SEM_WR BIT(0)
+#define SYNTH_MB_SEM_RD BIT(1)
+
+ZL3073X_REG16_DEF(synth_freq_base, 0x686);
+ZL3073X_REG32_DEF(synth_freq_mult, 0x688);
+ZL3073X_REG16_DEF(synth_freq_m, 0x68c);
+ZL3073X_REG16_DEF(synth_freq_n, 0x68e);
+
+/*
+ * Register Map Page 14, Output Mailbox
+ */
+ZL3073X_REG16_DEF(output_mb_mask, 0x702);
+ZL3073X_REG8_DEF(output_mb_sem, 0x704);
+#define OUTPUT_MB_SEM_WR BIT(0)
+#define OUTPUT_MB_SEM_RD BIT(1)
+
/*
* Regmap ranges
*/
@@ -142,6 +183,150 @@ int zl3073x_write_reg(struct zl3073x_dev *zldev, unsigned int reg,
}
EXPORT_SYMBOL_GPL(zl3073x_write_reg);

+/**
+ * ZL3073X_MB_OP - perform an operation on mailbox of certain type
+ * @_zldev: pointer to device structure
+ * @_type: type of mailbox (dpll, ref or output)
+ * @_index: object index of given type
+ * @_op: operation to perform
+ *
+ * Performs the requested operation on mailbox of certain type and
+ * returns 0 in case of success or negative value otherwise.
+ */
+#define ZL3073X_MB_OP(_zldev, _type, _index, _op) \
+({ \
+ struct zl3073x_dev *__zldev = (_zldev); \
+ u16 __mask = BIT(_index); \
+ u8 __op = (_op); \
+ int __rc; \
+ do { \
+ /* Select requested index in mask register */ \
+ __rc = zl3073x_write_##_type##_mb_mask(__zldev, __mask);\
+ if (__rc) \
+ break; \
+ /* Select requested command */ \
+ __rc = zl3073x_write_##_type##_mb_sem(__zldev, __op); \
+ if (__rc) \
+ break; \
+ /* Wait for the command to actually finish */ \
+ __rc = zl3073x_wait_clear_bits(__zldev, _type##_mb_sem, \
+ __op); \
+ } while (0); \
+ __rc; \
+})
+
+/**
+ * zl3073x_mb_dpll_read - read given DPLL configuration to mailbox
+ * @zldev: pointer to device structure
+ * @index: DPLL index
+ *
+ * Reads configuration of given DPLL into DPLL mailbox and returns 0
+ * in case of success or negative value otherwise.
+ */
+int zl3073x_mb_dpll_read(struct zl3073x_dev *zldev, u8 index)
+{
+ return ZL3073X_MB_OP(zldev, dpll, index, DPLL_MB_SEM_RD);
+}
+EXPORT_SYMBOL_GPL(zl3073x_mb_dpll_read);
+
+/**
+ * zl3073x_mb_dpll_write - write given DPLL configuration from mailbox
+ * @zldev: pointer to device structure
+ * @index: DPLL index
+ *
+ * Writes (commits) configuration of given DPLL from DPLL mailbox and
+ * returns 0 in case of success or negative value otherwise.
+ */
+int zl3073x_mb_dpll_write(struct zl3073x_dev *zldev, u8 index)
+{
+ return ZL3073X_MB_OP(zldev, dpll, index, DPLL_MB_SEM_WR);
+}
+EXPORT_SYMBOL_GPL(zl3073x_mb_dpll_write);
+
+/**
+ * zl3073x_mb_output_read - read given output configuration to mailbox
+ * @zldev: pointer to device structure
+ * @index: output index
+ *
+ * Reads configuration of given output into output mailbox and returns 0
+ * in case of success or negative value otherwise.
+ */
+int zl3073x_mb_output_read(struct zl3073x_dev *zldev, u8 index)
+{
+ return ZL3073X_MB_OP(zldev, output, index, OUTPUT_MB_SEM_RD);
+}
+EXPORT_SYMBOL_GPL(zl3073x_mb_output_read);
+
+/**
+ * zl3073x_mb_output_write - write given output configuration from mailbox
+ * @zldev: pointer to device structure
+ * @index: DPLL index
+ *
+ * Writes (commits) configuration of given output from output mailbox and
+ * returns 0 in case of success or negative value otherwise.
+ */
+int zl3073x_mb_output_write(struct zl3073x_dev *zldev, u8 index)
+{
+ return ZL3073X_MB_OP(zldev, output, index, OUTPUT_MB_SEM_WR);
+}
+EXPORT_SYMBOL_GPL(zl3073x_mb_output_write);
+
+/**
+ * zl3073x_mb_ref_read - read given reference configuration to mailbox
+ * @zldev: pointer to device structure
+ * @index: reference index
+ *
+ * Reads configuration of given reference into reference mailbox and
+ * returns 0 in case of success or negative value otherwise.
+ */
+int zl3073x_mb_ref_read(struct zl3073x_dev *zldev, u8 index)
+{
+ return ZL3073X_MB_OP(zldev, ref, index, REF_MB_SEM_RD);
+}
+EXPORT_SYMBOL_GPL(zl3073x_mb_ref_read);
+
+/**
+ * zl3073x_mb_ref_write - write given reference configuration from mailbox
+ * @zldev: pointer to device structure
+ * @index: reference index
+ *
+ * Writes (commits) configuration of given reference from reference mailbox
+ * and returns 0 in case of success or negative value otherwise.
+ */
+int zl3073x_mb_ref_write(struct zl3073x_dev *zldev, u8 index)
+{
+ return ZL3073X_MB_OP(zldev, ref, index, REF_MB_SEM_WR);
+}
+EXPORT_SYMBOL_GPL(zl3073x_mb_ref_write);
+
+/**
+ * zl3073x_mb_synth_read - read given synth configuration to mailbox
+ * @zldev: pointer to device structure
+ * @index: synth index
+ *
+ * Reads configuration of given synth into synth mailbox and returns 0
+ * in case of success or negative value otherwise.
+ */
+int zl3073x_mb_synth_read(struct zl3073x_dev *zldev, u8 index)
+{
+ return ZL3073X_MB_OP(zldev, synth, index, SYNTH_MB_SEM_RD);
+}
+EXPORT_SYMBOL_GPL(zl3073x_mb_synth_read);
+
+/**
+ * zl3073x_mb_synth_write - write given synth configuration from mailbox
+ * @zldev: pointer to device structure
+ * @index: synth index
+ *
+ * Writes (commits) configuration of given synth from reference mailbox
+ * and returns 0 in case of success or negative value otherwise.
+ */
+int zl3073x_mb_synth_write(struct zl3073x_dev *zldev, u8 index)
+{
+ return ZL3073X_MB_OP(zldev, synth, index, SYNTH_MB_SEM_WR);
+}
+EXPORT_SYMBOL_GPL(zl3073x_mb_synth_write);
+
/**
* zl3073x_devlink_info_get - Devlink device info callback
* @devlink: devlink structure pointer
diff --git a/include/linux/mfd/zl3073x.h b/include/linux/mfd/zl3073x.h
index 15dfb0d8bf3cb..436f79f2fda63 100644
--- a/include/linux/mfd/zl3073x.h
+++ b/include/linux/mfd/zl3073x.h
@@ -172,4 +172,16 @@ int zl3073x_write_##_name(struct zl3073x_dev *zldev, unsigned int idx, \
__rc; \
})

+/*
+ * Mailbox operations
+ */
+int zl3073x_mb_dpll_read(struct zl3073x_dev *zldev, u8 index);
+int zl3073x_mb_dpll_write(struct zl3073x_dev *zldev, u8 index);
+int zl3073x_mb_output_read(struct zl3073x_dev *zldev, u8 index);
+int zl3073x_mb_output_write(struct zl3073x_dev *zldev, u8 index);
+int zl3073x_mb_ref_read(struct zl3073x_dev *zldev, u8 index);
+int zl3073x_mb_ref_write(struct zl3073x_dev *zldev, u8 index);
+int zl3073x_mb_synth_read(struct zl3073x_dev *zldev, u8 index);
+int zl3073x_mb_synth_write(struct zl3073x_dev *zldev, u8 index);
+
#endif /* __LINUX_MFD_ZL3073X_H */
--
2.48.1