dpll: zl3073x: make embedded sync follow the output carrier frequency

The eSync period and width are relative to the output carrier, but
changing the output frequency did not update them, so eSync ran at
a wrong frequency and duty cycle. For a 1 Hz carrier esync_get()
returns -EOPNOTSUPP, so eSync left enabled could not be disabled.

Factor out zl3073x_out_esync_{is_enabled,enable,disable}() helpers
and use them in frequency_set() to recompute the eSync period and
width for the new carrier, or to disable eSync for a 1 Hz carrier.

Also compute the reported pulse width in 64 bits, as
50 * esync_n_width overflows u32 for low carrier frequencies.

Hit during development, confirmed on an oscilloscope.

Fixes: ce26d7ca50 ("dpll: zl3073x: Add support to get/set frequency on pins")
Fixes: 634ca2cb06 ("dpll: zl3073x: Add support to get/set esync on pins")
Reviewed-by: Petr Oros <poros@redhat.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Link: https://patch.msgid.link/20261011061211.448373-5-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Ivan Vecera 2026-10-11 08:12:10 +02:00 committed by Jakub Kicinski
parent 456027325c
commit bf6f105f2c
2 changed files with 60 additions and 22 deletions

View File

@ -878,7 +878,7 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
esync->range = esync_freq_ranges;
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
if (zl3073x_out_clock_type_get(out) != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
if (!zl3073x_out_esync_is_enabled(out)) {
/* No need to read esync data if it is not enabled */
esync->freq = 0;
esync->pulse = 0;
@ -894,7 +894,7 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
* 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 * out->esync_n_width) / out->div;
esync->pulse = div_u64(mul_u32_u32(50, out->esync_n_width), out->div);
return 0;
}
@ -926,32 +926,17 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
if (zl3073x_out_is_ndiv(&out))
return -EOPNOTSUPP;
/* Update clock type in output mode */
if (freq)
zl3073x_out_clock_type_set(&out,
ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC);
else
zl3073x_out_clock_type_set(&out,
ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL);
/* If esync is being disabled just write mailbox and finish */
if (!freq)
if (!freq) {
zl3073x_out_esync_disable(&out);
return zl3073x_out_state_set(zldev, out_id, &out);
}
/* Get attached synth frequency */
synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(&out));
synth_freq = zl3073x_synth_freq_get(synth);
/* Compute and update esync period */
out.esync_n_period = synth_freq / (u32)freq / out.div;
/* 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.
*/
out.esync_n_width = out.div / 2;
/* Enable 1PPS eSync for this pin frequency */
zl3073x_out_esync_enable(&out, synth_freq / out.div);
/* Commit output configuration */
return zl3073x_out_state_set(zldev, out_id, &out);
@ -1010,6 +995,23 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
/* For 50/50 duty cycle the divisor is equal to width */
out.width = new_div;
/* The embedded sync period and width are computed relative to
* the output carrier so they have to be adjusted to follow the
* new frequency.
*/
if (zl3073x_out_esync_is_enabled(&out)) {
if (frequency == 1) {
/* eSync cannot work on a 1 Hz carrier */
zl3073x_out_esync_disable(&out);
} else {
/* Update esync period and width according to
* the new frequency.
*/
zl3073x_out_esync_enable(&out,
synth_freq / out.div);
}
}
/* Commit output configuration */
return zl3073x_out_state_set(zldev, out_id, &out);
}

View File

@ -134,4 +134,40 @@ static inline u8 zl3073x_out_synth_get(const struct zl3073x_out *out)
return FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, out->ctrl);
}
static inline bool zl3073x_out_esync_is_enabled(const struct zl3073x_out *out)
{
u8 clock_type = zl3073x_out_clock_type_get(out);
return clock_type == ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC;
}
static inline
void zl3073x_out_esync_enable(struct zl3073x_out *out, u32 base_freq)
{
/* Period is expressed as the number of output divider clock cycles.
* So for 1 Hz the period is equal to base frequency.
*/
out->esync_n_period = base_freq;
/* 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.
*/
out->esync_n_width = out->div / 2;
/* Update the clock type */
zl3073x_out_clock_type_set(out, ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC);
}
static inline
void zl3073x_out_esync_disable(struct zl3073x_out *out)
{
/* Just update the clock type. The eSync period and width are
* ignored for this clock type.
*/
zl3073x_out_clock_type_set(out, ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL);
}
#endif /* _ZL3073X_OUT_H */