]> rtime.felk.cvut.cz Git - zynq/linux.git/blob - drivers/clk/si5324drv.c
3cd498b81644fe4eb5b603a40473f366458c2952
[zynq/linux.git] / drivers / clk / si5324drv.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Si5324 clock driver
4  *
5  * Copyright (C) 2017-2018 Xilinx, Inc.
6  *
7  * Author:      Venkateshwar Rao G <vgannava.xilinx.com>
8  *              Leon Woestenberg <leon@sidebranch.com>
9  */
10
11 #include <linux/module.h>
12 #include <linux/types.h>
13 #include "si5324drv.h"
14
15 /**
16  * si5324_rate_approx - Find closest rational approximation N2_LS/N3 fraction.
17  *
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.
22  *
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.
27  */
28 void si5324_rate_approx(u64 f, u64 md, u32 *num, u32 *denom)
29 {
30         u64 a, h[3] = { 0, 1, 0 }, k[3] = { 1, 0, 0 };
31         u64 x, d, n = 1;
32         int i = 0;
33
34         if (md <= 1) {
35                 *denom = 1;
36                 *num = (u32)(f >> 28);
37                 return;
38         }
39
40         n <<= 28;
41         for (i = 0; i < 28; i++) {
42                 if ((f & 0x1) == 0) {
43                         n >>= 1;
44                         f >>= 1;
45                 } else {
46                         break;
47                 }
48         }
49         d = f;
50
51         for (i = 0; i < 64; i++) {
52                 a = n ? d / n : 0;
53                 if (i && !a)
54                         break;
55                 x = d;
56                 d = n;
57                 n = x % n;
58
59                 x = a;
60                 if (k[1] * a + k[0] >= md) {
61                         x = (md - k[0]) / k[1];
62                         if (x * 2 >= a || k[1] >= md)
63                                 i = 65;
64                         else
65                                 break;
66                 }
67                 h[2] = x * h[1] + h[0];
68                 h[0] = h[1];
69                 h[1] = h[2];
70                 k[2] = x * k[1] + k[0];
71                 k[0] = k[1];
72                 k[1] = k[2];
73         }
74
75         *denom = (u32)k[1];
76         *num = (u32)h[1];
77 }
78
79 /**
80  * si5324_find_n2ls - Search through the possible settings for the N2_LS.
81  *
82  * @settings:   Holds the settings up till now.
83  *
84  * This function finds the best setting for N2_LS and N3n with the values
85  * for N1_HS, NCn_LS, and N2_HS.
86  *
87  * Return:      1 when the best possible result has been found, 0 on failure.
88  */
89 static int si5324_find_n2ls(struct si5324_settingst *settings)
90 {
91         u32 result = 0;
92         u64 f3_actual;
93         u64 fosc_actual;
94         u64 fout_actual;
95         u64 delta_fout;
96         u64 n2_ls_div_n3;
97         u32 mult;
98
99         n2_ls_div_n3 = settings->fosc /
100                         (settings->fin >> SI5324_FIN_FOUT_SHIFT) /
101                         settings->n2_hs / 2;
102         si5324_rate_approx(n2_ls_div_n3, settings->n31_max, &settings->n2_ls,
103                            &settings->n31);
104         settings->n2_ls *= 2;
105
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) ?
109                         mult + 1 : mult;
110                 settings->n2_ls *= mult;
111                 settings->n31 *= mult;
112         }
113
114         if (settings->n31 < settings->n31_min) {
115                 mult =  settings->n31_min / settings->n31;
116                 mult = (settings->n31_min % settings->n31) ?
117                         mult + 1 : mult;
118                 settings->n2_ls *= mult;
119                 settings->n31 *= mult;
120         }
121         pr_debug("Trying N2_LS = %d N3 = %d.\n", settings->n2_ls,
122                  settings->n31);
123
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");
130         } else {
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;
136
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");
152                 } else {
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) /
158                                        1000),
159                                  (u32)(f3_actual >> SI5324_FIN_FOUT_SHIFT));
160
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);
170                                 if (delta_fout == 0)
171                                         result = 1;
172                         }
173                 }
174         }
175         return result;
176 }
177
178 /**
179  * si5324_find_n2 - Find a valid setting for N2_HS and N2_LS.
180  *
181  * @settings:   Holds the settings up till now.
182  *
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
185  * N2_LS values.
186  *
187  * Return:      1 when the best possible result has been found.
188  */
189 static int si5324_find_n2(struct si5324_settingst *settings)
190 {
191         u32 result = 0;
192
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 *
198                                         settings->n2_hs) <<
199                                         SI5324_FIN_FOUT_SHIFT));
200
201                 if (settings->n2_ls_min < SI5324_N2_LS_MIN)
202                         settings->n2_ls_min = SI5324_N2_LS_MIN;
203
204                 settings->n2_ls_max = (u32)(settings->fosc /
205                                         ((u64)(SI5324_F3_MIN *
206                                         settings->n2_hs) <<
207                                         SI5324_FIN_FOUT_SHIFT));
208                 if (settings->n2_ls_max > SI5324_N2_LS_MAX)
209                         settings->n2_ls_max = SI5324_N2_LS_MAX;
210
211                 result = si5324_find_n2ls(settings);
212                 if (result)
213                         break;
214         }
215         return result;
216 }
217
218 /**
219  * si5324_calc_ncls_limits - Calculates the valid range for NCn_LS.
220  *
221  * @settings:   Holds the input and output frequencies and the setting
222  * for N1_HS.
223  *
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.
226  *
227  * Return:      -1 when there are no valid settings, 0 otherwise.
228  */
229 int si5324_calc_ncls_limits(struct si5324_settingst *settings)
230 {
231         settings->nc1_ls_min = settings->n1_hs_min / settings->n1_hs;
232
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;
238
239         if (settings->nc1_ls_max > SI5324_NC_LS_MAX)
240                 settings->nc1_ls_max = SI5324_NC_LS_MAX;
241
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))
246                 return -1;
247
248         return 0;
249 }
250
251 /**
252  * si5324_find_ncls - Find a valid setting for NCn_LS
253  *
254  * @settings:   Holds the input and output frequencies, the setting for
255  * N1_HS, and the limits for NCn_LS.
256  *
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).
260  *
261  * Return:      1 when the best possible result has been found.
262  */
263 static int si5324_find_ncls(struct si5324_settingst *settings)
264 {
265         u64 fosc_1;
266         u32 result;
267
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",
273                          settings->nc1_ls,
274                          (u32)((settings->fosc >> SI5324_FIN_FOUT_SHIFT) /
275                                1000));
276
277                 result = si5324_find_n2(settings);
278                 if (result)
279                         break;
280                 if (settings->nc1_ls == 1)
281                         settings->nc1_ls++;
282                 else
283                         settings->nc1_ls += 2;
284         }
285         return result;
286 }
287
288 /**
289  * si5324_calcfreqsettings - Calculate the frequency settings
290  *
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.
300  *
301  * This funciton calculates the frequency settings for the desired output
302  * frequency.
303  *
304  * Return:      SI5324_SUCCESS for success, SI5324_ERR_FREQ when the
305  * requested frequency cannot be generated.
306  */
307 int si5324_calcfreqsettings(u32 clkinfreq, u32 clkoutfreq, u32 *clkactual,
308                             u8 *n1_hs, u32 *ncn_ls, u8 *n2_hs, u32 *n2_ls,
309                             u32 *n3n, u8 *bwsel)
310 {
311         struct si5324_settingst settings;
312         int result;
313
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;
317
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;
321
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;
325
326         settings.n31_min = clkinfreq / SI5324_F3_MAX;
327         if (settings.n31_min < SI5324_N3_MIN)
328                 settings.n31_min = SI5324_N3_MIN;
329
330         settings.n31_max = clkinfreq / SI5324_F3_MIN;
331         if (settings.n31_max > SI5324_N3_MAX)
332                 settings.n31_max = SI5324_N3_MAX;
333
334         /* Find a valid oscillator frequency with the highest setting of N1_HS
335          * possible (reduces power)
336          */
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);
340
341                 result = si5324_calc_ncls_limits(&settings);
342                 if (result) {
343                         pr_debug("No valid settings\n");
344                         continue;
345                 }
346                 result = si5324_find_ncls(&settings);
347                 if (result)
348                         break;
349         }
350
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);
355
356         if (settings.best_delta_fout == settings.fout) {
357                 pr_debug("Si5324: No valid settings found.");
358                 return SI5324_ERR_FREQ;
359         }
360                 pr_debug("Si5324: Found solution: fout = %dHz.\n",
361                          (u32)(settings.best_fout >> 28));
362
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;
369         /*
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 (?)
374          */
375         *bwsel = 6;
376
377         if (clkactual)
378                 *clkactual = (settings.best_fout >> SI5324_FIN_FOUT_SHIFT);
379
380         return SI5324_SUCCESS;
381 }