1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2017-2018 Xilinx, Inc.
7 * Author: Venkateshwar Rao G <vgannava.xilinx.com>
8 * Leon Woestenberg <leon@sidebranch.com>
11 #include <linux/module.h>
12 #include <linux/types.h>
13 #include "si5324drv.h"
16 * si5324_rate_approx - Find closest rational approximation N2_LS/N3 fraction.
18 * @f: Holds the N2_LS/N3 fraction in 36.28 fixed point notation.
19 * @md: Holds the maximum denominator (N3) value allowed.
20 * @num: Store the numinator (N2_LS) found.
21 * @denom: Store the denominator (N3) found.
23 * This function finds the closest rational approximation.
24 * It allows only n/1 solution and as a part of the calculation
25 * multiply fraction until no digits after the decimal point and
26 * continued fraction and check denominator at each step.
28 void si5324_rate_approx(u64 f, u64 md, u32 *num, u32 *denom)
30 u64 a, h[3] = { 0, 1, 0 }, k[3] = { 1, 0, 0 };
36 *num = (u32)(f >> 28);
41 for (i = 0; i < 28; i++) {
51 for (i = 0; i < 64; i++) {
60 if (k[1] * a + k[0] >= md) {
61 x = (md - k[0]) / k[1];
62 if (x * 2 >= a || k[1] >= md)
67 h[2] = x * h[1] + h[0];
70 k[2] = x * k[1] + k[0];
80 * si5324_find_n2ls - Search through the possible settings for the N2_LS.
82 * @settings: Holds the settings up till now.
84 * This function finds the best setting for N2_LS and N3n with the values
85 * for N1_HS, NCn_LS, and N2_HS.
87 * Return: 1 when the best possible result has been found, 0 on failure.
89 static int si5324_find_n2ls(struct si5324_settingst *settings)
99 n2_ls_div_n3 = settings->fosc /
100 (settings->fin >> SI5324_FIN_FOUT_SHIFT) /
102 si5324_rate_approx(n2_ls_div_n3, settings->n31_max, &settings->n2_ls,
104 settings->n2_ls *= 2;
106 if (settings->n2_ls < settings->n2_ls_min) {
107 mult = settings->n2_ls_min / settings->n2_ls;
108 mult = (settings->n2_ls_min % settings->n2_ls) ?
110 settings->n2_ls *= mult;
111 settings->n31 *= mult;
114 if (settings->n31 < settings->n31_min) {
115 mult = settings->n31_min / settings->n31;
116 mult = (settings->n31_min % settings->n31) ?
118 settings->n2_ls *= mult;
119 settings->n31 *= mult;
121 pr_debug("Trying N2_LS = %d N3 = %d.\n", settings->n2_ls,
124 if (settings->n2_ls < settings->n2_ls_min ||
125 settings->n2_ls > settings->n2_ls_max) {
126 pr_info("N2_LS out of range.\n");
127 } else if ((settings->n31 < settings->n31_min) ||
128 (settings->n31 > settings->n31_max)) {
129 pr_info("N3 out of range.\n");
131 f3_actual = settings->fin / settings->n31;
132 fosc_actual = f3_actual * settings->n2_hs * settings->n2_ls;
133 fout_actual = fosc_actual /
134 (settings->n1_hs * settings->nc1_ls);
135 delta_fout = fout_actual - settings->fout;
137 if ((f3_actual < ((u64)SI5324_F3_MIN) <<
138 SI5324_FIN_FOUT_SHIFT) ||
139 (f3_actual > ((u64)SI5324_F3_MAX) <<
140 SI5324_FIN_FOUT_SHIFT)) {
141 pr_debug("F3 frequency out of range.\n");
142 } else if ((fosc_actual < ((u64)SI5324_FOSC_MIN) <<
143 SI5324_FIN_FOUT_SHIFT) ||
144 (fosc_actual > ((u64)SI5324_FOSC_MAX) <<
145 SI5324_FIN_FOUT_SHIFT)) {
146 pr_debug("Fosc frequency out of range.\n");
147 } else if ((fout_actual < ((u64)SI5324_FOUT_MIN) <<
148 SI5324_FIN_FOUT_SHIFT) ||
149 (fout_actual > ((u64)SI5324_FOUT_MAX) <<
150 SI5324_FIN_FOUT_SHIFT)) {
151 pr_debug("Fout frequency out of range.\n");
153 pr_debug("Found solution: fout = %dHz delta = %dHz.\n",
154 (u32)(fout_actual >> SI5324_FIN_FOUT_SHIFT),
155 (u32)(delta_fout >> SI5324_FIN_FOUT_SHIFT));
156 pr_debug("fosc = %dkHz f3 = %dHz.\n",
157 (u32)((fosc_actual >> SI5324_FIN_FOUT_SHIFT) /
159 (u32)(f3_actual >> SI5324_FIN_FOUT_SHIFT));
161 if (((u64)abs(delta_fout)) <
162 settings->best_delta_fout) {
163 settings->best_n1_hs = settings->n1_hs;
164 settings->best_nc1_ls = settings->nc1_ls;
165 settings->best_n2_hs = settings->n2_hs;
166 settings->best_n2_ls = settings->n2_ls;
167 settings->best_n3 = settings->n31;
168 settings->best_fout = fout_actual;
169 settings->best_delta_fout = abs(delta_fout);
179 * si5324_find_n2 - Find a valid setting for N2_HS and N2_LS.
181 * @settings: Holds the settings up till now.
183 * This function finds a valid settings for N2_HS and N2_LS. Iterates over
184 * all possibilities of N2_HS and then performs a binary search over the
187 * Return: 1 when the best possible result has been found.
189 static int si5324_find_n2(struct si5324_settingst *settings)
193 for (settings->n2_hs = SI5324_N2_HS_MAX; settings->n2_hs >=
194 SI5324_N2_HS_MIN; settings->n2_hs--) {
195 pr_debug("Trying N2_HS = %d.\n", settings->n2_hs);
196 settings->n2_ls_min = (u32)(settings->fosc /
197 ((u64)(SI5324_F3_MAX *
199 SI5324_FIN_FOUT_SHIFT));
201 if (settings->n2_ls_min < SI5324_N2_LS_MIN)
202 settings->n2_ls_min = SI5324_N2_LS_MIN;
204 settings->n2_ls_max = (u32)(settings->fosc /
205 ((u64)(SI5324_F3_MIN *
207 SI5324_FIN_FOUT_SHIFT));
208 if (settings->n2_ls_max > SI5324_N2_LS_MAX)
209 settings->n2_ls_max = SI5324_N2_LS_MAX;
211 result = si5324_find_n2ls(settings);
219 * si5324_calc_ncls_limits - Calculates the valid range for NCn_LS.
221 * @settings: Holds the input and output frequencies and the setting
224 * This function calculates the valid range for NCn_LS with the value
225 * for the output frequency and N1_HS already set in settings.
227 * Return: -1 when there are no valid settings, 0 otherwise.
229 int si5324_calc_ncls_limits(struct si5324_settingst *settings)
231 settings->nc1_ls_min = settings->n1_hs_min / settings->n1_hs;
233 if (settings->nc1_ls_min < SI5324_NC_LS_MIN)
234 settings->nc1_ls_min = SI5324_NC_LS_MIN;
235 if (settings->nc1_ls_min > 1 && (settings->nc1_ls_min & 0x1) == 1)
236 settings->nc1_ls_min++;
237 settings->nc1_ls_max = settings->n1_hs_max / settings->n1_hs;
239 if (settings->nc1_ls_max > SI5324_NC_LS_MAX)
240 settings->nc1_ls_max = SI5324_NC_LS_MAX;
242 if ((settings->nc1_ls_max & 0x1) == 1)
243 settings->nc1_ls_max--;
244 if ((settings->nc1_ls_max * settings->n1_hs < settings->n1_hs_min) ||
245 (settings->nc1_ls_min * settings->n1_hs > settings->n1_hs_max))
252 * si5324_find_ncls - Find a valid setting for NCn_LS
254 * @settings: Holds the input and output frequencies, the setting for
255 * N1_HS, and the limits for NCn_LS.
257 * This function find a valid setting for NCn_LS that can deliver the correct
258 * output frequency. Assumes that the valid range is relatively small
259 * so a full search can be done (should be true for video clock frequencies).
261 * Return: 1 when the best possible result has been found.
263 static int si5324_find_ncls(struct si5324_settingst *settings)
268 fosc_1 = settings->fout * settings->n1_hs;
269 for (settings->nc1_ls = settings->nc1_ls_min;
270 settings->nc1_ls <= settings->nc1_ls_max;) {
271 settings->fosc = fosc_1 * settings->nc1_ls;
272 pr_debug("Trying NCn_LS = %d: fosc = %dkHz.\n",
274 (u32)((settings->fosc >> SI5324_FIN_FOUT_SHIFT) /
277 result = si5324_find_n2(settings);
280 if (settings->nc1_ls == 1)
283 settings->nc1_ls += 2;
289 * si5324_calcfreqsettings - Calculate the frequency settings
291 * @clkinfreq: Frequency of the input clock.
292 * @clkoutfreq: Desired output clock frequency.
293 * @clkactual: Actual clock frequency.
294 * @n1_hs: Set to the value for the N1_HS register.
295 * @ncn_ls: Set to the value for the NCn_LS register.
296 * @n2_hs: Set to the value for the N2_HS register.
297 * @n2_ls: Set to the value for the N2_LS register.
298 * @n3n: Set to the value for the N3n register.
299 * @bwsel: Set to the value for the BW_SEL register.
301 * This funciton calculates the frequency settings for the desired output
304 * Return: SI5324_SUCCESS for success, SI5324_ERR_FREQ when the
305 * requested frequency cannot be generated.
307 int si5324_calcfreqsettings(u32 clkinfreq, u32 clkoutfreq, u32 *clkactual,
308 u8 *n1_hs, u32 *ncn_ls, u8 *n2_hs, u32 *n2_ls,
311 struct si5324_settingst settings;
314 settings.fin = (u64)clkinfreq << SI5324_FIN_FOUT_SHIFT;
315 settings.fout = (u64)clkoutfreq << SI5324_FIN_FOUT_SHIFT;
316 settings.best_delta_fout = settings.fout;
318 settings.n1_hs_min = (int)(SI5324_FOSC_MIN / clkoutfreq);
319 if (settings.n1_hs_min < SI5324_N1_HS_MIN * SI5324_NC_LS_MIN)
320 settings.n1_hs_min = SI5324_N1_HS_MIN * SI5324_NC_LS_MIN;
322 settings.n1_hs_max = (int)(SI5324_FOSC_MAX / clkoutfreq);
323 if (settings.n1_hs_max > SI5324_N1_HS_MAX * SI5324_NC_LS_MAX)
324 settings.n1_hs_max = SI5324_N1_HS_MAX * SI5324_NC_LS_MAX;
326 settings.n31_min = clkinfreq / SI5324_F3_MAX;
327 if (settings.n31_min < SI5324_N3_MIN)
328 settings.n31_min = SI5324_N3_MIN;
330 settings.n31_max = clkinfreq / SI5324_F3_MIN;
331 if (settings.n31_max > SI5324_N3_MAX)
332 settings.n31_max = SI5324_N3_MAX;
334 /* Find a valid oscillator frequency with the highest setting of N1_HS
335 * possible (reduces power)
337 for (settings.n1_hs = SI5324_N1_HS_MAX;
338 settings.n1_hs >= SI5324_N1_HS_MIN; settings.n1_hs--) {
339 pr_debug("Trying N1_HS = %d.\n", settings.n1_hs);
341 result = si5324_calc_ncls_limits(&settings);
343 pr_debug("No valid settings\n");
346 result = si5324_find_ncls(&settings);
351 pr_debug("Si5324: settings.best_delta_fout = %llu\n",
352 (unsigned long long)settings.best_delta_fout);
353 pr_debug("Si5324: settings.fout = %llu\n",
354 (unsigned long long)settings.fout);
356 if (settings.best_delta_fout == settings.fout) {
357 pr_debug("Si5324: No valid settings found.");
358 return SI5324_ERR_FREQ;
360 pr_debug("Si5324: Found solution: fout = %dHz.\n",
361 (u32)(settings.best_fout >> 28));
363 /* Post processing: convert temporary values to actual registers */
364 *n1_hs = (u8)settings.best_n1_hs - 4;
365 *ncn_ls = settings.best_nc1_ls - 1;
366 *n2_hs = (u8)settings.best_n2_hs - 4;
367 *n2_ls = settings.best_n2_ls - 1;
368 *n3n = settings.best_n3 - 1;
370 * How must the bandwidth selection be determined?
371 * Not all settings will be valid.
372 * refclk 2, 0xA2, BWSEL_REG=1010 (?)
373 * free running 2, 0x42, BWSEL_REG=0100 (?)
378 *clkactual = (settings.best_fout >> SI5324_FIN_FOUT_SHIFT);
380 return SI5324_SUCCESS;