[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250424154722.534284-6-ivecera@redhat.com>
Date: Thu, 24 Apr 2025 17:47:19 +0200
From: Ivan Vecera <ivecera@...hat.com>
To: netdev@...r.kernel.org
Cc: Vadim Fedorenko <vadim.fedorenko@...ux.dev>,
Arkadiusz Kubalewski <arkadiusz.kubalewski@...el.com>,
Jiri Pirko <jiri@...nulli.us>,
Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>,
Prathosh Satish <Prathosh.Satish@...rochip.com>,
Lee Jones <lee@...nel.org>,
Kees Cook <kees@...nel.org>,
Andy Shevchenko <andy@...nel.org>,
Andrew Morton <akpm@...ux-foundation.org>,
Michal Schmidt <mschmidt@...hat.com>,
devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-hardening@...r.kernel.org
Subject: [PATCH net-next v4 5/8] mfd: zl3073x: Add functions to work with register mailboxes
Registers located on page 10 and above are called mailbox-type
registers. Each page represents a mailbox and is used to read from
and write to configuration of a specific object (DPLL, output,
reference or synth).
Each mailbox page contains a mask register, which selects an index of
the target object to interact with and a semaphore register, which
indicates the requested operation.
The remaining registers within the page are latch registers, which are
populated by the firmware during read operations or by the driver prior
to write operations.
Define structures for sending and receiving mailbox content for each
supported object types, along with functions to handle reading and
writing.
Currently, no locking is required, as only MFD driver accesses these
registers. This will change in future.
Signed-off-by: Ivan Vecera <ivecera@...hat.com>
---
v3->v4:
* completely reworked mailbox access
v1->v3:
* dropped ZL3073X_MB_OP macro usage
---
drivers/mfd/zl3073x-core.c | 420 ++++++++++++++++++++++++++++++++++++
drivers/mfd/zl3073x-regs.h | 64 ++++++
include/linux/mfd/zl3073x.h | 141 ++++++++++++
3 files changed, 625 insertions(+)
diff --git a/drivers/mfd/zl3073x-core.c b/drivers/mfd/zl3073x-core.c
index 28cc25b7412e9..4f7c915f17980 100644
--- a/drivers/mfd/zl3073x-core.c
+++ b/drivers/mfd/zl3073x-core.c
@@ -170,6 +170,426 @@ zl3073x_read_reg(struct zl3073x_dev *zldev, unsigned int reg, void *val)
return rc;
}
+static int
+zl3073x_write_reg(struct zl3073x_dev *zldev, unsigned int reg, const void *val)
+{
+ unsigned int len;
+ u8 buf[6];
+ int rc;
+
+ /* Offset of the last item in the indexed register or offset of
+ * the non-indexed register itself.
+ */
+ if (ZL_REG_OFFSET(reg) > ZL_REG_MAX_OFFSET(reg)) {
+ dev_err(zldev->dev, "Index of out range for reg 0x%04lx\n",
+ ZL_REG_ADDR(reg));
+ return -EINVAL;
+ }
+
+ len = ZL_REG_SIZE(reg);
+ switch (len) {
+ case 1:
+ buf[0] = *(u8 *)val;
+ break;
+ case 2:
+ put_unaligned_be16(*(u16 *)val, buf);
+ break;
+ case 4:
+ put_unaligned_be32(*(u32 *)val, buf);
+ break;
+ case 6:
+ put_unaligned_be48(*(u64 *)val, buf);
+ break;
+ default:
+ dev_err(zldev->dev, "Invalid reg-width %u for reg 0x%04lx\n",
+ len, ZL_REG_ADDR(reg));
+ return -EINVAL;
+ }
+
+ /* Map the register address to virtual range */
+ reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET;
+
+ rc = regmap_bulk_write(zldev->regmap, reg, buf, len);
+ if (rc) {
+ dev_err(zldev->dev, "Failed to write reg 0x%04x: %pe\n", reg,
+ ERR_PTR(rc));
+ return rc;
+ }
+
+ return rc;
+}
+
+static int
+zl3073x_wait_reg_zero_bits(struct zl3073x_dev *zldev, unsigned int reg, u8 mask)
+{
+ /* Register polling sleep & timeout */
+#define ZL_POLL_SLEEP_US 10
+#define ZL_POLL_TIMEOUT_US 2000000
+ unsigned int val;
+
+ /* Only 8bit registers are supported */
+ BUILD_BUG_ON(ZL_REG_SIZE(reg) != 1);
+
+ /* Map the register address to virtual range for polling */
+ reg = ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET;
+
+ return regmap_read_poll_timeout(zldev->regmap, reg, val, !(val & mask),
+ ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US);
+}
+
+static int
+zl3073x_mb_cmd_do(struct zl3073x_dev *zldev, unsigned int cmd_reg, u8 cmd,
+ unsigned int mask_reg, u16 mask)
+{
+ int rc;
+
+ rc = zl3073x_write_reg(zldev, mask_reg, &mask);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_write_reg(zldev, cmd_reg, &cmd);
+ if (rc)
+ return rc;
+
+ /* Wait for the command to finish */
+ return zl3073x_wait_reg_zero_bits(zldev, cmd_reg, cmd);
+}
+
+/**
+ * zl3073x_mb_dpll_read - read given DPLL configuration to mailbox
+ * @zldev: pointer to device structure
+ * @index: DPLL index
+ * @fields: mask of the mailbox fields to be filled
+ * @mb: DPLL mailbox
+ *
+ * Reads selected configuration of given reference into output mailbox.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_mb_dpll_read(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_dpll *mb)
+{
+ int i, rc;
+
+ rc = zl3073x_mb_cmd_do(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
+ ZL_REG_DPLL_MB_MASK, BIT(index));
+ if (rc)
+ return rc;
+
+ for (i = 0; i < ARRAY_SIZE(mb->ref_prio); i++) {
+ if (fields & BIT(i)) {
+ rc = zl3073x_read_reg(zldev, ZL_REG_DPLL_REF_PRIO(i),
+ &mb->ref_prio[i]);
+ if (rc)
+ break;
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(zl3073x_mb_dpll_read, "ZL3073X");
+
+/**
+ * zl3073x_mb_dpll_write - write given DPLL configuration from mailbox
+ * @zldev: pointer to device structure
+ * @index: DPLL index
+ * @fields: mask of the mailbox fields to be written
+ * @mb: DPLL channel mailbox
+ *
+ * Writes selected fields from the mailbox into device.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_mb_dpll_write(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_dpll *mb)
+{
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(mb->ref_prio); i++) {
+ if (fields & BIT(i)) {
+ rc = zl3073x_write_reg(zldev, ZL_REG_DPLL_REF_PRIO(i),
+ &mb->ref_prio[i]);
+ if (rc)
+ break;
+ }
+ }
+
+ return zl3073x_mb_cmd_do(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,
+ ZL_REG_DPLL_MB_MASK, BIT(index));
+}
+EXPORT_SYMBOL_NS_GPL(zl3073x_mb_dpll_write, "ZL3073X");
+
+/**
+ * zl3073x_mb_output_read - read given output configuration to mailbox
+ * @zldev: pointer to device structure
+ * @index: output index
+ * @fields: mask of the mailbox fields to be filled
+ * @mb: output mailbox
+ *
+ * Reads selected configuration of given reference into output mailbox.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_mb_output_read(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_output *mb)
+{
+ int rc;
+
+ rc = zl3073x_mb_cmd_do(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
+ ZL_REG_OUTPUT_MB_MASK, BIT(index));
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_MODE))
+ rc = zl3073x_read_reg(zldev, ZL_REG_OUTPUT_MODE, &mb->mode);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_DIV))
+ rc = zl3073x_read_reg(zldev, ZL_REG_OUTPUT_DIV, &mb->div);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_WIDTH))
+ rc = zl3073x_read_reg(zldev, ZL_REG_OUTPUT_WIDTH, &mb->width);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_ESYNC_PERIOD))
+ rc = zl3073x_read_reg(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
+ &mb->esync_period);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_ESYNC_WIDTH))
+ rc = zl3073x_read_reg(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH,
+ &mb->esync_width);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_PHASE_COMP))
+ rc = zl3073x_read_reg(zldev, ZL_REG_OUTPUT_PHASE_COMP,
+ &mb->phase_comp);
+
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(zl3073x_mb_output_read, "ZL3073X");
+
+/**
+ * zl3073x_mb_output_write - write given output configuration from mailbox
+ * @zldev: pointer to device structure
+ * @index: output index
+ * @fields: mask of the mailbox fields to be written
+ * @mb: output mailbox
+ *
+ * Writes selected fields from the mailbox into device.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_mb_output_write(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ const struct zl3073x_mb_output *mb)
+{
+ int rc = 0;
+
+ if (fields & ZL3073X_MB_OUTPUT_MODE)
+ rc = zl3073x_write_reg(zldev, ZL_REG_OUTPUT_MODE, &mb->mode);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_DIV))
+ rc = zl3073x_write_reg(zldev, ZL_REG_OUTPUT_DIV, &mb->div);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_WIDTH))
+ rc = zl3073x_write_reg(zldev, ZL_REG_OUTPUT_WIDTH, &mb->width);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_ESYNC_PERIOD))
+ rc = zl3073x_write_reg(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
+ &mb->esync_period);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_ESYNC_WIDTH))
+ rc = zl3073x_write_reg(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH,
+ &mb->esync_width);
+
+ if (!rc && (fields & ZL3073X_MB_OUTPUT_PHASE_COMP))
+ rc = zl3073x_write_reg(zldev, ZL_REG_OUTPUT_PHASE_COMP,
+ &mb->phase_comp);
+ if (rc)
+ return rc;
+
+ return zl3073x_mb_cmd_do(zldev,
+ ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
+ ZL_REG_OUTPUT_MB_MASK, BIT(index));
+}
+EXPORT_SYMBOL_NS_GPL(zl3073x_mb_output_write, "ZL3073X");
+
+/**
+ * zl3073x_mb_ref_read - read given reference configuration to mailbox
+ * @zldev: pointer to device structure
+ * @index: reference index
+ * @fields: mask of the mailbox fields to be filled
+ * @mb: reference mailbox
+ *
+ * Reads selected configuration of given reference into ref mailbox.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_mb_ref_read(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_ref *mb)
+{
+ int rc;
+
+ rc = zl3073x_mb_cmd_do(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
+ ZL_REG_REF_MB_MASK, BIT(index));
+
+ if (!rc && (fields & ZL3073X_MB_REF_FREQ_BASE))
+ rc = zl3073x_read_reg(zldev, ZL_REG_REF_FREQ_BASE,
+ &mb->freq_base);
+
+ if (!rc && (fields & ZL3073X_MB_REF_FREQ_MULT))
+ rc = zl3073x_read_reg(zldev, ZL_REG_REF_FREQ_MULT,
+ &mb->freq_mult);
+
+ if (!rc && (fields & ZL3073X_MB_REF_RATIO_M))
+ rc = zl3073x_read_reg(zldev, ZL_REG_REF_RATIO_M, &mb->ratio_m);
+
+ if (!rc && (fields & ZL3073X_MB_REF_RATIO_N))
+ rc = zl3073x_read_reg(zldev, ZL_REG_REF_RATIO_N, &mb->ratio_n);
+
+ if (!rc && (fields & ZL3073X_MB_REF_CONFIG))
+ rc = zl3073x_read_reg(zldev, ZL_REG_REF_CONFIG, &mb->config);
+
+ if (!rc && (fields & ZL3073X_MB_REF_PHASE_OFFSET_COMP))
+ rc = zl3073x_read_reg(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
+ &mb->phase_offset_comp);
+
+ if (!rc && (fields & ZL3073X_MB_REF_SYNC_CTRL))
+ rc = zl3073x_read_reg(zldev, ZL_REG_REF_SYNC_CTRL,
+ &mb->sync_ctrl);
+
+ if (!rc && (fields & ZL3073X_MB_REF_ESYNC_DIV))
+ rc = zl3073x_read_reg(zldev, ZL_REG_REF_ESYNC_DIV,
+ &mb->esync_div);
+
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(zl3073x_mb_ref_read, "ZL3073X");
+
+/**
+ * zl3073x_mb_ref_write - write given reference configuration from mailbox
+ * @zldev: pointer to device structure
+ * @index: reference index
+ * @fields: mask of the mailbox fields to be written
+ * @mb: reference mailbox
+ *
+ * Writes selected fields from the mailbox into device.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_mb_ref_write(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ const struct zl3073x_mb_ref *mb)
+{
+ int rc = 0;
+
+ if (fields & ZL3073X_MB_REF_FREQ_BASE)
+ rc = zl3073x_write_reg(zldev, ZL_REG_REF_FREQ_BASE,
+ &mb->freq_base);
+
+ if (!rc && (fields & ZL3073X_MB_REF_FREQ_MULT))
+ rc = zl3073x_write_reg(zldev, ZL_REG_REF_FREQ_MULT,
+ &mb->freq_mult);
+
+ if (!rc && (fields & ZL3073X_MB_REF_RATIO_M))
+ rc = zl3073x_write_reg(zldev, ZL_REG_REF_RATIO_M,
+ &mb->ratio_m);
+
+ if (!rc && (fields & ZL3073X_MB_REF_RATIO_N))
+ rc = zl3073x_write_reg(zldev, ZL_REG_REF_RATIO_N,
+ &mb->ratio_n);
+
+ if (!rc && (fields & ZL3073X_MB_REF_CONFIG))
+ rc = zl3073x_write_reg(zldev, ZL_REG_REF_CONFIG, &mb->config);
+
+ if (!rc && (fields & ZL3073X_MB_REF_PHASE_OFFSET_COMP))
+ rc = zl3073x_write_reg(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
+ &mb->phase_offset_comp);
+
+ if (!rc && (fields & ZL3073X_MB_REF_SYNC_CTRL))
+ rc = zl3073x_write_reg(zldev, ZL_REG_REF_SYNC_CTRL,
+ &mb->sync_ctrl);
+
+ if (!rc && (fields & ZL3073X_MB_REF_ESYNC_DIV))
+ rc = zl3073x_write_reg(zldev, ZL_REG_REF_ESYNC_DIV,
+ &mb->esync_div);
+
+ if (rc)
+ return rc;
+
+ return zl3073x_mb_cmd_do(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
+ ZL_REG_REF_MB_MASK, BIT(index));
+}
+EXPORT_SYMBOL_NS_GPL(zl3073x_mb_ref_write, "ZL3073X");
+
+/**
+ * zl3073x_mb_synth_read - read given synth configuration to mailbox
+ * @zldev: pointer to device structure
+ * @index: synth index
+ * @fields: mask of the mailbox fields to be filled
+ * @mb: synth mailbox
+ *
+ * Reads selected configuration of given reference into synth mailbox.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_mb_synth_read(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_synth *mb)
+{
+ int rc;
+
+ rc = zl3073x_mb_cmd_do(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
+ ZL_REG_SYNTH_MB_MASK, BIT(index));
+
+ if (!rc && (fields & ZL3073X_MB_SYNTH_FREQ_BASE))
+ rc = zl3073x_read_reg(zldev, ZL_REG_SYNTH_FREQ_BASE,
+ &mb->freq_base);
+
+ if (!rc && (fields & ZL3073X_MB_SYNTH_FREQ_MULT))
+ rc = zl3073x_read_reg(zldev, ZL_REG_SYNTH_FREQ_MULT,
+ &mb->freq_mult);
+
+ if (!rc && (fields & ZL3073X_MB_SYNTH_FREQ_M))
+ rc = zl3073x_read_reg(zldev, ZL_REG_SYNTH_FREQ_M, &mb->freq_m);
+
+ if (!rc && (fields & ZL3073X_MB_SYNTH_FREQ_N))
+ rc = zl3073x_read_reg(zldev, ZL_REG_SYNTH_FREQ_N, &mb->freq_n);
+
+ return rc;
+}
+EXPORT_SYMBOL_NS_GPL(zl3073x_mb_synth_read, "ZL3073X");
+
+/**
+ * zl3073x_mb_synth_write - write given synth configuration from mailbox
+ * @zldev: pointer to device structure
+ * @index: synth index
+ * @fields: mask of the mailbox fields to be written
+ * @mb: synth mailbox
+ *
+ * Writes selected fields from the mailbox into device.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_mb_synth_write(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_synth *mb)
+{
+ int rc = 0;
+
+ if (fields & ZL3073X_MB_SYNTH_FREQ_BASE)
+ rc = zl3073x_write_reg(zldev, ZL_REG_SYNTH_FREQ_BASE,
+ &mb->freq_base);
+
+ if (!rc && (fields & ZL3073X_MB_SYNTH_FREQ_MULT))
+ rc = zl3073x_write_reg(zldev, ZL_REG_SYNTH_FREQ_MULT,
+ &mb->freq_mult);
+
+ if (!rc && (fields & ZL3073X_MB_SYNTH_FREQ_M))
+ rc = zl3073x_write_reg(zldev, ZL_REG_SYNTH_FREQ_M, &mb->freq_m);
+
+ if (!rc && (fields & ZL3073X_MB_SYNTH_FREQ_N))
+ rc = zl3073x_write_reg(zldev, ZL_REG_SYNTH_FREQ_N, &mb->freq_n);
+
+ if (rc)
+ return rc;
+
+ return zl3073x_mb_cmd_do(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_WR,
+ ZL_REG_SYNTH_MB_MASK, BIT(index));
+}
+EXPORT_SYMBOL_NS_GPL(zl3073x_mb_synth_write, "ZL3073X");
+
/**
* zl3073x_devlink_info_get - Devlink device info callback
* @devlink: devlink structure pointer
diff --git a/drivers/mfd/zl3073x-regs.h b/drivers/mfd/zl3073x-regs.h
index 3a8fcc860a6ea..a19f04c813cc6 100644
--- a/drivers/mfd/zl3073x-regs.h
+++ b/drivers/mfd/zl3073x-regs.h
@@ -71,4 +71,68 @@
#define ZL_REG_FW_VER ZL_REG(0, 0x05, 2)
#define ZL_REG_CUSTOM_CONFIG_VER ZL_REG(0, 0x07, 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_FREQ_BASE ZL_REG(10, 0x05, 2)
+#define ZL_REG_REF_FREQ_MULT ZL_REG(10, 0x07, 2)
+#define ZL_REG_REF_RATIO_M ZL_REG(10, 0x09, 2)
+#define ZL_REG_REF_RATIO_N ZL_REG(10, 0x0b, 2)
+#define ZL_REG_REF_CONFIG ZL_REG(10, 0x0d, 1)
+#define ZL_REG_REF_PHASE_OFFSET_COMP ZL_REG(10, 0x28, 6)
+#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1)
+#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4)
+
+/********************************
+ * Register Page 12, DPLL Mailbox
+ ********************************/
+
+#define ZL_REG_DPLL_MB_MASK ZL_REG(12, 0x02, 2)
+
+#define ZL_REG_DPLL_MB_SEM ZL_REG(12, 0x04, 1)
+#define ZL_DPLL_MB_SEM_WR BIT(0)
+#define ZL_DPLL_MB_SEM_RD BIT(1)
+
+#define ZL_REG_DPLL_REF_PRIO(_idx) \
+ ZL_REG_IDX(_idx, 12, 0x52, 1, ZL3073X_NUM_INPUTS / 2, 1)
+
+/*********************************
+ * 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_REG_OUTPUT_DIV ZL_REG(14, 0x0c, 4)
+#define ZL_REG_OUTPUT_WIDTH ZL_REG(14, 0x10, 4)
+#define ZL_REG_OUTPUT_ESYNC_PERIOD ZL_REG(14, 0x14, 4)
+#define ZL_REG_OUTPUT_ESYNC_WIDTH ZL_REG(14, 0x18, 4)
+#define ZL_REG_OUTPUT_PHASE_COMP ZL_REG(14, 0x20, 4)
+
#endif /* __ZL3073X_REGS_H */
diff --git a/include/linux/mfd/zl3073x.h b/include/linux/mfd/zl3073x.h
index ad5344a84b320..352cd0f2f64a4 100644
--- a/include/linux/mfd/zl3073x.h
+++ b/include/linux/mfd/zl3073x.h
@@ -8,6 +8,13 @@
struct device;
struct regmap;
+/*
+ * Hardware limits for ZL3073x chip family
+ */
+#define ZL3073X_NUM_INPUTS 10
+#define ZL3073X_NUM_OUTPUTS 10
+#define ZL3073X_NUM_SYNTHS 5
+
/**
* struct zl3073x_dev - zl3073x device
* @dev: pointer to device
@@ -18,4 +25,138 @@ struct zl3073x_dev {
struct regmap *regmap;
};
+/*************************
+ * DPLL mailbox operations
+ *************************/
+
+/**
+ * struct zl3073x_mb_dpll - DPLL channel mailbox
+ * @ref_prio: array of input reference priorities
+ */
+struct zl3073x_mb_dpll {
+ u8 ref_prio[ZL3073X_NUM_INPUTS / 2]; /* 4bits per ref */
+#define ZL_DPLL_REF_PRIO_REF_P GENMASK(3, 0)
+#define ZL_DPLL_REF_PRIO_REF_N GENMASK(7, 4)
+#define ZL_DPLL_REF_PRIO_MAX 14
+#define ZL_DPLL_REF_PRIO_NONE 15
+};
+#define ZL3073X_MB_DPLL_REF_PRIO(_ref_pair) BIT(_ref_pair)
+
+int zl3073x_mb_dpll_read(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_dpll *mb);
+int zl3073x_mb_dpll_write(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_dpll *mb);
+
+/***************************
+ * Output mailbox operations
+ ***************************/
+
+/**
+ * struct zl3073x_mb_output - output mailbox
+ * @mode: output mode
+ * @div: output divisor
+ * @width: output width
+ * @esync_period: embedded sync period
+ * @esync_width: embedded sync width
+ * @phase_comp: phase compensation
+ */
+struct zl3073x_mb_output {
+ u8 mode; /* page 14, offset 0x05 */
+#define ZL_OUTPUT_MODE_CLOCK_TYPE GENMASK(2, 0)
+#define ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL 0
+#define ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC 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
+ u32 div; /* page 14, offset 0x0c */
+ u32 width; /* page 14, offset 0x10 */
+ u32 esync_period; /* page 14, offset 0x14 */
+ u32 esync_width; /* page 14, offset 0x18 */
+ u32 phase_comp; /* page 14, offset 0x20 */
+};
+#define ZL3073X_MB_OUTPUT_MODE BIT(0)
+#define ZL3073X_MB_OUTPUT_DIV BIT(1)
+#define ZL3073X_MB_OUTPUT_WIDTH BIT(2)
+#define ZL3073X_MB_OUTPUT_ESYNC_PERIOD BIT(3)
+#define ZL3073X_MB_OUTPUT_ESYNC_WIDTH BIT(4)
+#define ZL3073X_MB_OUTPUT_PHASE_COMP BIT(5)
+
+int zl3073x_mb_output_read(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_output *output);
+int zl3073x_mb_output_write(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ const struct zl3073x_mb_output *output);
+
+/************************************
+ * Input reference mailbox operations
+ ************************************/
+
+/**
+ * struct zl3073x_mb_ref - input reference mailbox
+ * @freq_base: frequency base
+ * @freq_mult: frequency multiplier
+ * @ratio_m: FEC ratio numerator
+ * @ratio_n: FEC ratio denominator
+ * @config: reference configuration
+ * @phase_offset_comp: phase offset compensation
+ * @sync_ctrl: synchronization control
+ * @esync_div: embedded sync divisor
+ */
+struct zl3073x_mb_ref {
+ u16 freq_base; /* page 10, offset 0x05 */
+ u16 freq_mult; /* page 10, offset 0x07 */
+ u16 ratio_m; /* page 10, offset 0x09 */
+ u16 ratio_n; /* page 10, offset 0x0b */
+ u8 config; /* page 10, offset 0x0d */
+#define ZL_REF_CONFIG_ENABLE BIT(0)
+#define ZL_REF_CONFIG_DIFF_EN BIT(2)
+ u64 phase_offset_comp; /* page 10, offset 0x28 */
+ u8 sync_ctrl; /* page 10, offset 0x2e */
+#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)
+#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0
+#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2
+ u32 esync_div; /* page 10, offset 0x30 */
+#define ZL_REF_ESYNC_DIV_1HZ 0
+};
+#define ZL3073X_MB_REF_FREQ_BASE BIT(0)
+#define ZL3073X_MB_REF_FREQ_MULT BIT(1)
+#define ZL3073X_MB_REF_RATIO_M BIT(2)
+#define ZL3073X_MB_REF_RATIO_N BIT(3)
+#define ZL3073X_MB_REF_CONFIG BIT(4)
+#define ZL3073X_MB_REF_PHASE_OFFSET_COMP BIT(5)
+#define ZL3073X_MB_REF_SYNC_CTRL BIT(6)
+#define ZL3073X_MB_REF_ESYNC_DIV BIT(7)
+
+int zl3073x_mb_ref_read(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_ref *ref);
+int zl3073x_mb_ref_write(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ const struct zl3073x_mb_ref *ref);
+
+/**************************
+ * Synth mailbox operations
+ **************************/
+
+struct zl3073x_mb_synth {
+ u16 freq_base; /* page 13, offset 0x06 */
+ u32 freq_mult; /* page 13, offset 0x08 */
+ u16 freq_m; /* page 13, offset 0x0c */
+ u16 freq_n; /* page 13, offset 0x0e */
+};
+#define ZL3073X_MB_SYNTH_FREQ_BASE BIT(0)
+#define ZL3073X_MB_SYNTH_FREQ_MULT BIT(1)
+#define ZL3073X_MB_SYNTH_FREQ_M BIT(2)
+#define ZL3073X_MB_SYNTH_FREQ_N BIT(3)
+
+int zl3073x_mb_synth_read(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_synth *mb);
+int zl3073x_mb_synth_write(struct zl3073x_dev *zldev, u8 index, u32 fields,
+ struct zl3073x_mb_synth *mb);
+
#endif /* __LINUX_MFD_ZL3073X_H */
--
2.49.0
Powered by blists - more mailing lists