@@ -14,6 +14,20 @@ ZL3073X_REG16_DEF(revision, 0x0003);
ZL3073X_REG16_DEF(fw_ver, 0x0005);
ZL3073X_REG32_DEF(custom_config_ver, 0x0007);
+/*
+ * Register Map Page 9, Synth and Output
+ */
+ZL3073X_REG8_IDX_DEF(synth_ctrl, 0x480, ZL3073X_NUM_SYNTHS, 1);
+#define SYNTH_CTRL_EN BIT(0)
+#define SYNTH_CTRL_DPLL_SEL GENMASK(6, 4)
+
+ZL3073X_REG8_IDX_DEF(output_ctrl, 0x4a8, ZL3073X_NUM_OUTPUTS, 1);
+#define OUTPUT_CTRL_EN BIT(0)
+#define OUTPUT_CTRL_STOP BIT(1)
+#define OUTPUT_CTRL_STOP_HIGH BIT(2)
+#define OUTPUT_CTRL_STOP_HZ BIT(3)
+#define OUTPUT_CTRL_SYNTH_SEL GENMASK(6, 4)
+
/*
* Register Map Page 10, Ref Mailbox
*/
@@ -24,6 +38,10 @@ ZL3073X_REG8_DEF(ref_mb_sem, 0x504);
#define REF_MB_SEM_WR BIT(0)
#define REF_MB_SEM_RD BIT(1)
+ZL3073X_REG8_DEF(ref_config, 0x50d);
+#define REF_CONFIG_ENABLE BIT(0)
+#define REF_CONFIG_DIFF_EN BIT(2)
+
/*
* Register Map Page 12, DPLL Mailbox
*/
@@ -55,6 +73,9 @@ ZL3073X_REG8_DEF(output_mb_sem, 0x704);
#define OUTPUT_MB_SEM_WR BIT(0)
#define OUTPUT_MB_SEM_RD BIT(1)
+ZL3073X_REG8_DEF(output_mode, 0x705);
+#define OUTPUT_MODE_SIGNAL_FORMAT GENMASK(7, 4)
+
/*
* Regmap ranges
*/
@@ -526,6 +547,219 @@ static void zl3073x_fw_load(struct zl3073x_dev *zldev)
release_firmware(fw);
}
+/**
+ * zl3073x_input_state_fetch - get input state
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: input pin index to fetch state for
+ *
+ * Function fetches information for the given input reference that are
+ * invariant and stores them for later use.
+ *
+ * Returns 0 in case of success or negative value in error case.
+ */
+static int
+zl3073x_input_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+ struct zl3073x_input *input;
+ u8 ref_config;
+ int rc;
+
+ if (index >= ZL3073X_NUM_INPUTS)
+ return -EINVAL;
+
+ input = &zldev->input[index];
+
+ /* 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_input_is_diff(zldev, index-1)) {
+ input->enabled = zl3073x_input_is_enabled(zldev, index-1);
+ input->diff = true;
+
+ return 0;
+ }
+
+ /* Read reference configuration into mailbox */
+ rc = zl3073x_mb_ref_read(zldev, index);
+ if (rc)
+ return rc;
+
+ /* Read reference config register */
+ rc = zl3073x_read_ref_config(zldev, &ref_config);
+ if (rc)
+ return rc;
+
+ /* Store info about input reference enablement and if it is
+ * configured in differential mode or not.
+ */
+ input->enabled = FIELD_GET(REF_CONFIG_ENABLE, ref_config);
+ input->diff = FIELD_GET(REF_CONFIG_DIFF_EN, ref_config);
+
+ return rc;
+}
+
+/**
+ * zl3073x_output_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.
+ *
+ * Returns 0 in case of success or negative value in error case.
+ */
+static int
+zl3073x_output_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+ struct zl3073x_output *output;
+ u8 output_ctrl, output_mode;
+ int rc;
+
+ if (index >= ZL3073X_NUM_OUTPUTS)
+ return -EINVAL;
+
+ output = &zldev->output[index];
+
+ /* Read output control register */
+ rc = zl3073x_read_output_ctrl(zldev, index, &output_ctrl);
+ if (rc)
+ return rc;
+
+ /* Store info about output enablement and synthesizer the output
+ * is connected to.
+ */
+ output->enabled = FIELD_GET(OUTPUT_CTRL_EN, output_ctrl);
+ output->synth = FIELD_GET(OUTPUT_CTRL_SYNTH_SEL, output_ctrl);
+
+ /* Load given output config into mailbox */
+ rc = zl3073x_mb_output_read(zldev, index);
+ if (rc)
+ return rc;
+
+ /* Read output mode from mailbox */
+ rc = zl3073x_read_output_mode(zldev, &output_mode);
+ if (rc)
+ return rc;
+
+ /* Extract and store output signal format */
+ output->signal_format = FIELD_GET(OUTPUT_MODE_SIGNAL_FORMAT,
+ output_mode);
+
+ 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.
+ *
+ * Returns 0 in case of success or negative value in error case.
+ */
+static int
+zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+ u16 base, numerator, denominator;
+ u8 synth_ctrl;
+ u32 mult;
+ int rc;
+
+ /* Read synth control register */
+ rc = zl3073x_read_synth_ctrl(zldev, index, &synth_ctrl);
+ if (rc)
+ return rc;
+
+ /* Extract and store DPLL channel the synth is driven by */
+ zldev->synth[index].dpll = FIELD_GET(SYNTH_CTRL_DPLL_SEL, synth_ctrl);
+
+ dev_dbg(zldev->dev, "SYNTH%u is connected to DPLL%u\n", index,
+ zldev->synth[index].dpll);
+
+ /* Read synth configuration into mailbox */
+ rc = zl3073x_mb_synth_read(zldev, index);
+ if (rc)
+ return rc;
+
+ /* The output frequency is determined by the following formula:
+ * base * multiplier * numerator / denominator
+ *
+ * Therefore get all this number and calculate the output frequency
+ */
+ rc = zl3073x_read_synth_freq_base(zldev, &base);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_read_synth_freq_mult(zldev, &mult);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_read_synth_freq_m(zldev, &numerator);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_read_synth_freq_n(zldev, &denominator);
+ if (rc)
+ return rc;
+
+ /* Check denominator for zero to avoid div by 0 */
+ if (!denominator) {
+ dev_err(zldev->dev,
+ "Zero divisor for SYNTH%u retrieved from device\n",
+ index);
+ return -ENODEV;
+ }
+
+ /* Compute and store synth frequency */
+ zldev->synth[index].freq = mul_u64_u32_div(mul_u32_u32(base, mult),
+ numerator, denominator);
+
+ dev_dbg(zldev->dev, "SYNTH%u frequency: %llu 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_INPUTS; i++) {
+ rc = zl3073x_input_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_OUTPUTS; i++) {
+ rc = zl3073x_output_state_fetch(zldev, i);
+ if (rc) {
+ dev_err(zldev->dev,
+ "Failed to fetch output state: %pe\n",
+ ERR_PTR(rc));
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
int zl3073x_dev_init(struct zl3073x_dev *zldev, u8 dev_id)
{
u16 id, revision, fw_ver;
@@ -556,6 +790,13 @@ int zl3073x_dev_init(struct zl3073x_dev *zldev, u8 dev_id)
/* Load mfg file if present */
zl3073x_fw_load(zldev);
+ /* Fetch device state */
+ scoped_guard(zl3073x, zldev) {
+ rc = zl3073x_dev_state_fetch(zldev);
+ if (rc)
+ return rc;
+ }
+
dev_info(zldev->dev, "ChipID(%X), ChipRev(%X), FwVer(%u)\n",
id, revision, fw_ver);
dev_info(zldev->dev, "Custom config version: %lu.%lu.%lu.%lu\n",
@@ -6,11 +6,49 @@
#include <linux/device.h>
#include <linux/regmap.h>
+/*
+ * Hardware limits for ZL3073x chip family
+ */
+#define ZL3073X_NUM_INPUTS 10
+#define ZL3073X_NUM_OUTPUTS 10
+#define ZL3073X_NUM_SYNTHS 5
+
+struct zl3073x_input {
+ bool enabled;
+ bool diff;
+};
+
+struct zl3073x_output {
+ bool enabled;
+ u8 synth;
+ u8 signal_format;
+#define OUTPUT_MODE_SIGNAL_FORMAT_DISABLED 0
+#define OUTPUT_MODE_SIGNAL_FORMAT_LVDS 1
+#define OUTPUT_MODE_SIGNAL_FORMAT_DIFFERENTIAL 2
+#define OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM 3
+#define OUTPUT_MODE_SIGNAL_FORMAT_TWO 4
+#define OUTPUT_MODE_SIGNAL_FORMAT_ONE_P 5
+#define OUTPUT_MODE_SIGNAL_FORMAT_ONE_N 6
+#define OUTPUT_MODE_SIGNAL_FORMAT_TWO_INV 7
+#define OUTPUT_MODE_SIGNAL_FORMAT_TWO_N_DIV 12
+#define OUTPUT_MODE_SIGNAL_FORMAT_TWO_N_DIV_INV 15
+};
+
+struct zl3073x_synth {
+ u64 freq;
+ u8 dpll;
+};
+
struct zl3073x_dev {
struct device *dev;
struct regmap *regmap;
u64 clock_id;
struct mutex lock;
+
+ /* Invariants */
+ struct zl3073x_input input[ZL3073X_NUM_INPUTS];
+ struct zl3073x_output output[ZL3073X_NUM_OUTPUTS];
+ struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
};
/**
@@ -185,4 +223,108 @@ 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);
+static inline
+bool zl3073x_is_n_pin(u8 index)
+{
+ /* P-pins indices are even while N-pins are odd */
+ return index & 1;
+}
+
+static inline
+bool zl3073x_is_p_pin(u8 index)
+{
+ return !zl3073x_is_n_pin(index);
+}
+
+/**
+ * zl3073x_input_is_diff - check if the given input ref is differential
+ * @zldev: device structure pointer
+ * @index: output index
+ *
+ * Returns true if the given input ref is differential
+ */
+static inline
+bool zl3073x_input_is_diff(struct zl3073x_dev *zldev, u8 index)
+{
+ return zldev->input[index].diff;
+}
+
+/**
+ * zl3073x_input_is_enabled - check if the given input ref is enabled
+ * @zldev: device structure pointer
+ * @index: output index
+ *
+ * Returns true if the given input ref is enabled
+ */
+static inline
+bool zl3073x_input_is_enabled(struct zl3073x_dev *zldev, u8 index)
+{
+ return zldev->input[index].enabled;
+}
+
+/**
+ * zl3073x_output_is_enabled - check if the given output is enabled
+ * @zldev: device structure pointer
+ * @index: output index
+ *
+ * Returns true if the given output is enabled
+ */
+static inline
+u8 zl3073x_output_is_enabled(struct zl3073x_dev *zldev, u8 index)
+{
+ return zldev->output[index].enabled;
+}
+
+/**
+ * zl3073x_output_signal_format_get - get output signal format
+ * @zldev: device structure pointer
+ * @index: output index
+ *
+ * Returns signal format of given output
+ */
+static inline
+u8 zl3073x_output_signal_format_get(struct zl3073x_dev *zldev, u8 index)
+{
+ return zldev->output[index].signal_format;
+}
+
+/**
+ * zl3073x_output_synth_get - get synth connected to given output
+ * @zldev: device structure pointer
+ * @index: output index
+ *
+ * Returns index of synth connected to given output.
+ */
+static inline
+u8 zl3073x_output_synth_get(struct zl3073x_dev *zldev, u8 index)
+{
+ return zldev->output[index].synth;
+}
+
+/**
+ * zl3073x_synth_dpll_get - get dpll id where the synth is connected to
+ * @zldev: device structure pointer
+ * @index: synth order number
+ *
+ * Returns frequency of given synthetizer
+ */
+static inline
+u64 zl3073x_synth_dpll_get(struct zl3073x_dev *zldev, u8 index)
+{
+ return zldev->synth[index].dpll;
+}
+
+/**
+ * zl3073x_synth_freq_get - get synth current freq
+ * @zldev: device structure pointer
+ * @index: synth order number
+ *
+ * Returns frequency of given synthetizer
+ */
+static inline
+u64 zl3073x_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
+{
+ return zldev->synth[index].freq;
+}
+
#endif /* __LINUX_MFD_ZL3073X_H */