diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index f2e137475b40..7a617d9bd671 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -987,6 +987,7 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin, const struct zl3073x_synth *synth; u32 new_div, synth_freq; struct zl3073x_out out; + u64 n_period, rem; u8 out_id; guard(mutex)(&zldpll->lock); @@ -1014,16 +1015,11 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin, } if (zl3073x_dpll_is_p_pin(pin)) { - /* We are going to change output frequency for P-pin but - * if the requested frequency is less than current N-pin - * frequency then indicate a failure as we are not able - * to compute N-pin divisor to keep its frequency unchanged. - * - * Update divisor for N-pin to keep N-pin frequency. + /* Keep the N-pin frequency: the new P-pin frequency has to be + * a multiple of it, at least twice as high. */ - out.esync_n_period = (out.esync_n_period * out.div) / new_div; - if (!out.esync_n_period) - return -EINVAL; + n_period = mul_u32_u32(out.esync_n_period, out.div); + n_period = div64_u64_rem(n_period, new_div, &rem); /* Update the output divisor */ out.div = new_div; @@ -1031,17 +1027,31 @@ 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 = out.div; } else { - /* We are going to change frequency of N-pin but if - * the requested freq is greater or equal than freq of P-pin - * in the output pair we cannot compute divisor for the N-pin. - * In this case indicate a failure. - * - * Update divisor for N-pin + /* The N-pin frequency has to divide the P-pin frequency and + * be at most half of it. */ - out.esync_n_period = div64_u64(synth_freq, frequency * out.div); - if (!out.esync_n_period) - return -EINVAL; + n_period = frequency * out.div; + n_period = div64_u64_rem(synth_freq, n_period, &rem); } + if (n_period < 2) { + NL_SET_ERR_MSG_FMT(extack, + "OUT%uN freq must be at most half of OUT%uP freq", + out_id, out_id); + return -EINVAL; + } + if (n_period > U32_MAX) { + NL_SET_ERR_MSG_FMT(extack, + "OUT%uN freq is too low for OUT%uP freq", + out_id, out_id); + return -EINVAL; + } + if (rem != 0) { + NL_SET_ERR_MSG_FMT(extack, + "OUT%uN freq must divide OUT%uP freq", + out_id, out_id); + return -EINVAL; + } + out.esync_n_period = n_period; /* For 50/50 duty cycle the divisor is equal to width */ out.esync_n_width = out.esync_n_period;