@@ -81,6 +81,14 @@ ZL3073X_REG16_DEF(ref_ratio_m, 0x509);
ZL3073X_REG16_DEF(ref_ratio_n, 0x50b);
ZL3073X_REG48_DEF(ref_phase_compensation, 0x528);
+ZL3073X_REG8_DEF(ref_sync_ctrl, 0x52e);
+#define REF_SYNC_CTRL_MODE GENMASK(2, 0)
+#define REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0
+#define REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2
+
+ZL3073X_REG32_DEF(ref_esync_div, 0x530);
+#define REF_ESYNC_DIV_1HZ 0
+
/*
* Register Map Page 12, DPLL Mailbox
*/
@@ -94,10 +102,17 @@ ZL3073X_REG8_IDX_DEF(dpll_ref_prio, 0x652,
/*
* Register Map Page 14, Output Mailbox
*/
+ZL3073X_REG8_DEF(output_mode, 0x705);
+#define OUTPUT_MODE_CLOCK_TYPE GENMASK(2, 0)
+#define OUTPUT_MODE_CLOCK_TYPE_NORMAL 0
+#define OUTPUT_MODE_CLOCK_TYPE_ESYNC 1
+
ZL3073X_REG32_DEF(output_div, 0x70c);
ZL3073X_REG32_DEF(output_width, 0x710);
-ZL3073X_REG32_DEF(output_ndiv_period, 0x714);
-ZL3073X_REG32_DEF(output_ndiv_width, 0x718);
+ZL3073X_REG32_DEF(output_esync_period, 0x714);
+ZL3073X_REG32_DEF(output_ndiv_period, 0x714); /* alias for previous */
+ZL3073X_REG32_DEF(output_esync_width, 0x718);
+ZL3073X_REG32_DEF(output_ndiv_width, 0x718); /* alias for previous */
ZL3073X_REG32_DEF(output_phase_compensation, 0x720);
@@ -122,6 +137,7 @@ struct zl3073x_dpll_pin_info {
* @index: index in zl3073x_dpll.pins array
* @prio: pin priority <0, 14>
* @selectable: pin is selectable in automatic mode
+ * @esync_control: embedded sync is controllable
* @pin_state: last saved pin state
* @phase_offset: last saved pin phase offset
*/
@@ -130,6 +146,7 @@ struct zl3073x_dpll_pin {
u8 index;
u8 prio;
bool selectable;
+ bool esync_control;
enum dpll_pin_state pin_state;
s64 phase_offset;
};
@@ -213,6 +230,13 @@ zl3073x_dpll_is_n_pin(struct zl3073x_dpll_pin *pin)
return zl3073x_is_n_pin(zl3073x_dpll_pin_index_get(pin));
}
+/*
+ * Supported esync ranges for input and for output per output pair type
+ */
+static const struct dpll_pin_frequency esync_freq_ranges[] = {
+ DPLL_PIN_FREQUENCY_RANGE(0, 1),
+};
+
/**
* zl3073x_dpll_is_p_pin - check if the pin is P-pin
* @pin: pin to check
@@ -352,6 +376,128 @@ zl3073x_dpll_input_ref_frequency_get(struct zl3073x_dev *zldev, u8 ref_id,
return rc;
}
+static int
+zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ struct dpll_pin_esync *esync,
+ struct netlink_ext_ack *extack)
+{
+ struct zl3073x_dpll *zldpll = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ u8 sync_mode, ref_id, ref_sync_ctrl;
+ u32 esync_div;
+ u64 ref_freq;
+ int rc;
+
+ /* Take device lock */
+ guard(zl3073x)(zldev);
+
+ /* Get index of the pin */
+ ref_id = zl3073x_dpll_pin_index_get(pin);
+
+ /* Get reference frequency */
+ rc = zl3073x_dpll_input_ref_frequency_get(zldev, ref_id, &ref_freq);
+ if (rc)
+ return rc;
+
+ /* Get ref sync mode */
+ rc = zl3073x_read_ref_sync_ctrl(zldev, &ref_sync_ctrl);
+ if (rc)
+ return rc;
+
+ sync_mode = FIELD_GET(REF_SYNC_CTRL_MODE, ref_sync_ctrl);
+
+ /* Get esync divisor */
+ rc = zl3073x_read_ref_esync_div(zldev, &esync_div);
+ if (rc)
+ return rc;
+
+ switch (sync_mode) {
+ case REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75:
+ esync->freq = (esync_div == REF_ESYNC_DIV_1HZ) ? 1 : 0;
+ esync->pulse = 25;
+ break;
+ default:
+ esync->freq = 0;
+ esync->pulse = 50;
+ break;
+ }
+
+ /* If the pin supports esync control expose its range but only
+ * if the current reference frequency is > 1 Hz.
+ */
+ if (pin->esync_control && ref_freq > 1) {
+ esync->range = esync_freq_ranges;
+ esync->range_num = ARRAY_SIZE(esync_freq_ranges);
+ } else {
+ esync->range = NULL;
+ esync->range_num = 0;
+ }
+
+ return rc;
+}
+
+static int
+zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 freq,
+ struct netlink_ext_ack *extack)
+{
+ struct zl3073x_dpll *zldpll = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ u8 ref_id, ref_sync_ctrl, sync_mode;
+ int rc;
+
+ /* Take device lock */
+ guard(zl3073x)(zldev);
+
+ /* Get index of the pin */
+ ref_id = zl3073x_dpll_pin_index_get(pin);
+
+ /* Read reference configuration into mailbox */
+ rc = zl3073x_mb_ref_read(zldev, ref_id);
+ if (rc)
+ return rc;
+
+ /* Read ref sync control */
+ rc = zl3073x_read_ref_sync_ctrl(zldev, &ref_sync_ctrl);
+ if (rc)
+ return rc;
+
+ /* Use freq == 0 to disable esync */
+ if (!freq)
+ sync_mode = REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF;
+ else
+ sync_mode = REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75;
+
+ ref_sync_ctrl &= REF_SYNC_CTRL_MODE;
+ ref_sync_ctrl |= FIELD_PREP(REF_SYNC_CTRL_MODE, sync_mode);
+
+ /* Update ref sync control register */
+ rc = zl3073x_write_ref_sync_ctrl(zldev, ref_sync_ctrl);
+ if (rc)
+ return rc;
+
+ if (freq) {
+ /* 1 Hz is only supported frequnecy currently */
+ rc = zl3073x_write_ref_esync_div(zldev, REF_ESYNC_DIV_1HZ);
+ if (rc)
+ return rc;
+ }
+
+ /* Update reference configuration from mailbox */
+ rc = zl3073x_mb_ref_write(zldev, ref_id);
+ if (rc)
+ return rc;
+
+ return rc;
+}
+
static int
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
@@ -1017,6 +1163,218 @@ zl3073x_dpll_pin_synth_get(struct zl3073x_dpll_pin *pin)
return zl3073x_output_synth_get(pin_to_dev(pin), output);
}
+static int
+zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ struct dpll_pin_esync *esync,
+ struct netlink_ext_ack *extack)
+{
+ struct zl3073x_dpll *zldpll = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ u32 esync_period, esync_width, output_div;
+ u8 clock_type, output, output_mode, synth;
+ u64 synth_freq;
+ int rc;
+
+ output = zl3073x_dpll_output_pin_output_get(pin);
+
+ /* If N-division is enabled, esync is not supported. The register used
+ * for N-division is also used for the esync divider so both cannot
+ * be used.
+ */
+ switch (zl3073x_output_signal_format_get(zldev, output)) {
+ case OUTPUT_MODE_SIGNAL_FORMAT_TWO_N_DIV:
+ case OUTPUT_MODE_SIGNAL_FORMAT_TWO_N_DIV_INV:
+ return -EOPNOTSUPP;
+ default:
+ break;
+ }
+
+ /* Take device lock */
+ guard(zl3073x)(zldev);
+
+ /* Read output configuration into mailbox */
+ rc = zl3073x_mb_output_read(zldev, output);
+ if (rc)
+ return rc;
+
+ /* Read output mode */
+ rc = zl3073x_read_output_mode(zldev, &output_mode);
+ if (rc)
+ return rc;
+
+ /* Read output divisor */
+ rc = zl3073x_read_output_div(zldev, &output_div);
+ if (rc)
+ return rc;
+
+ /* Check output divisor for zero */
+ if (!output_div) {
+ dev_err(zldev->dev,
+ "Zero divisor for OUTPUT%u got from device\n",
+ output);
+ return -EINVAL;
+ }
+
+ /* Get synth attached to output pin */
+ synth = zl3073x_dpll_pin_synth_get(pin);
+
+ /* Get synth frequency */
+ synth_freq = zl3073x_synth_freq_get(zldev, synth);
+
+ clock_type = FIELD_GET(OUTPUT_MODE_CLOCK_TYPE, output_mode);
+ if (clock_type != OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
+ /* No need to read esync data if it is not enabled */
+ esync->freq = 0;
+ esync->pulse = 50;
+
+ goto finish;
+ }
+
+ /* Read esync period */
+ rc = zl3073x_read_output_esync_period(zldev, &esync_period);
+ if (rc)
+ return rc;
+
+ /* Check esync divisor for zero */
+ if (!esync_period) {
+ dev_err(zldev->dev,
+ "Zero esync divisor for OUTPUT%u got from device\n",
+ output);
+ return -EINVAL;
+ }
+
+ /* Get esync pulse width in units of half synth cycles */
+ rc = zl3073x_read_output_esync_width(zldev, &esync_width);
+ if (rc)
+ return rc;
+
+ /* Compute esync frequency:
+ * esync_freq = synth_freq / output_div / esync_period;
+ * ... = synth_freq / (output_div * esync_period);
+ */
+ esync->freq = div64_u64(synth_freq,
+ mul_u32_u32(output_div, esync_period));
+
+ /* By comparing the esync_pulse_width to the half of the pulse width
+ * the esync pulse percentage can be determined.
+ * Note that half pulse width is in units of half synth cycles, which
+ * is why it reduces down to be output_div.
+ */
+ esync->pulse = (50 * esync_width) / output_div;
+
+finish:
+ /* Set supported esync ranges if the pin supports esync control and
+ * if the output frequency is > 1 Hz.
+ */
+ if (pin->esync_control && div_u64(synth_freq, output_div) > 1) {
+ esync->range = esync_freq_ranges;
+ esync->range_num = ARRAY_SIZE(esync_freq_ranges);
+ } else {
+ esync->range = NULL;
+ esync->range_num = 0;
+ }
+
+ return 0;
+}
+
+static int
+zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 freq,
+ struct netlink_ext_ack *extack)
+{
+ struct zl3073x_dpll *zldpll = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ u32 esync_period, esync_width, output_div;
+ u8 clock_type, output, output_mode, synth;
+ u64 synth_freq;
+ int rc;
+
+ output = zl3073x_dpll_output_pin_output_get(pin);
+
+ /* If N-division is enabled, esync is not supported. The register used
+ * for N-division is also used for the esync divider so both cannot
+ * be used.
+ */
+ switch (zl3073x_output_signal_format_get(zldev, output)) {
+ case OUTPUT_MODE_SIGNAL_FORMAT_TWO_N_DIV:
+ case OUTPUT_MODE_SIGNAL_FORMAT_TWO_N_DIV_INV:
+ return -EOPNOTSUPP;
+ default:
+ break;
+ }
+
+ /* Take device lock */
+ guard(zl3073x)(zldev);
+
+ /* Read output configuration into mailbox */
+ rc = zl3073x_mb_output_read(zldev, output);
+ if (rc)
+ return rc;
+
+ /* Read output mode */
+ rc = zl3073x_read_output_mode(zldev, &output_mode);
+ if (rc)
+ return rc;
+
+ /* Select clock type */
+ if (freq)
+ clock_type = OUTPUT_MODE_CLOCK_TYPE_ESYNC;
+ else
+ clock_type = OUTPUT_MODE_CLOCK_TYPE_NORMAL;
+
+ /* Update clock type in ref sync control */
+ output_mode &= ~OUTPUT_MODE_CLOCK_TYPE;
+ output_mode |= FIELD_PREP(OUTPUT_MODE_CLOCK_TYPE, clock_type);
+ rc = zl3073x_write_output_mode(zldev, output_mode);
+ if (rc)
+ return rc;
+
+ /* If esync is being disabled just write mailbox and finish */
+ if (!freq)
+ goto write_mailbox;
+
+ /* Read output divisor */
+ rc = zl3073x_read_output_div(zldev, &output_div);
+ if (rc)
+ return rc;
+
+ /* Get synth attached to output pin */
+ synth = zl3073x_dpll_pin_synth_get(pin);
+
+ /* Get synth frequency */
+ synth_freq = zl3073x_synth_freq_get(zldev, synth);
+
+ /* Compute and update esync period */
+ esync_period = (u32)div64_u64(synth_freq, freq * output_div);
+ rc = zl3073x_write_output_esync_period(zldev, esync_period);
+ if (rc)
+ return rc;
+
+ /* Half of the period in units of 1/2 synth cycle can be represented by
+ * the output_div. To get the supported esync pulse width of 25% of the
+ * period the output_div can just be divided by 2. Note that this
+ * assumes that output_div is even, otherwise some resolution will be
+ * lost.
+ */
+ esync_width = output_div / 2;
+ rc = zl3073x_write_output_esync_width(zldev, esync_width);
+ if (rc)
+ return rc;
+
+write_mailbox:
+ /* Update output configuration from mailbox */
+ rc = zl3073x_mb_output_write(zldev, output);
+
+ return rc;
+}
+
static int
zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
@@ -1418,6 +1776,8 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
.direction_get = zl3073x_dpll_pin_direction_get,
+ .esync_get = zl3073x_dpll_input_pin_esync_get,
+ .esync_set = zl3073x_dpll_input_pin_esync_set,
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
@@ -1431,6 +1791,8 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
.direction_get = zl3073x_dpll_pin_direction_get,
+ .esync_get = zl3073x_dpll_output_pin_esync_get,
+ .esync_set = zl3073x_dpll_output_pin_esync_set,
.frequency_get = zl3073x_dpll_output_pin_frequency_get,
.frequency_set = zl3073x_dpll_output_pin_frequency_set,
.phase_adjust_get = zl3073x_dpll_output_pin_phase_adjust_get,
@@ -1678,6 +2040,10 @@ zl3073x_dpll_pin_info_get(struct zl3073x_dpll_pin *pin)
pin_type);
}
+ /* Check if the pin supports embedded sync control */
+ pin->esync_control = fwnode_property_read_bool(pin_info->fwnode,
+ "esync-control");
+
/* Read supported frequencies property if it is specified */
num_freqs = fwnode_property_count_u64(pin_info->fwnode,
"supported-frequencies");