From bf6f105f2cb6477e36a9312477d0aa32a43aa13d Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Sun, 11 Oct 2026 08:12:10 +0200 Subject: [PATCH] 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: ce26d7ca50a5 ("dpll: zl3073x: Add support to get/set frequency on pins") Fixes: 634ca2cb06d2 ("dpll: zl3073x: Add support to get/set esync on pins") Reviewed-by: Petr Oros Signed-off-by: Ivan Vecera Link: https://patch.msgid.link/20261011061211.448373-5-ivecera@redhat.com Signed-off-by: Jakub Kicinski --- drivers/dpll/zl3073x/dpll.c | 46 +++++++++++++++++++------------------ drivers/dpll/zl3073x/out.h | 36 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 7a617d9bd671..6d9a6d21d30b 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -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); } diff --git a/drivers/dpll/zl3073x/out.h b/drivers/dpll/zl3073x/out.h index 660889c57bff..d2b8b4eb5a76 100644 --- a/drivers/dpll/zl3073x/out.h +++ b/drivers/dpll/zl3073x/out.h @@ -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 */