mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
456027325c
commit
bf6f105f2c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user