hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/drivers/clk/clk-axi-clkgen.c
....@@ -1,11 +1,9 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * AXI clkgen driver
34 *
45 * Copyright 2012-2013 Analog Devices Inc.
56 * Author: Lars-Peter Clausen <lars@metafoo.de>
6
- *
7
- * Licensed under the GPL-2.
8
- *
97 */
108
119 #include <linux/platform_device.h>
....@@ -29,19 +27,23 @@
2927
3028 #define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
3129
30
+#define MMCM_REG_CLKOUT5_2 0x07
3231 #define MMCM_REG_CLKOUT0_1 0x08
3332 #define MMCM_REG_CLKOUT0_2 0x09
33
+#define MMCM_REG_CLKOUT6_2 0x13
3434 #define MMCM_REG_CLK_FB1 0x14
3535 #define MMCM_REG_CLK_FB2 0x15
3636 #define MMCM_REG_CLK_DIV 0x16
3737 #define MMCM_REG_LOCK1 0x18
3838 #define MMCM_REG_LOCK2 0x19
3939 #define MMCM_REG_LOCK3 0x1a
40
+#define MMCM_REG_POWER 0x28
4041 #define MMCM_REG_FILTER1 0x4e
4142 #define MMCM_REG_FILTER2 0x4f
4243
4344 #define MMCM_CLKOUT_NOCOUNT BIT(6)
4445
46
+#define MMCM_CLK_DIV_DIVIDE BIT(11)
4547 #define MMCM_CLK_DIV_NOCOUNT BIT(12)
4648
4749 struct axi_clkgen {
....@@ -109,6 +111,8 @@
109111 unsigned long d, d_min, d_max, _d_min, _d_max;
110112 unsigned long m, m_min, m_max;
111113 unsigned long f, dout, best_f, fvco;
114
+ unsigned long fract_shift = 0;
115
+ unsigned long fvco_min_fract, fvco_max_fract;
112116
113117 fin /= 1000;
114118 fout /= 1000;
....@@ -121,42 +125,89 @@
121125 d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
122126 d_max = min_t(unsigned long, fin / fpfd_min, 80);
123127
124
- m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
125
- m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
128
+again:
129
+ fvco_min_fract = fvco_min << fract_shift;
130
+ fvco_max_fract = fvco_max << fract_shift;
131
+
132
+ m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1);
133
+ m_max = min_t(unsigned long, fvco_max_fract * d_max / fin, 64 << fract_shift);
126134
127135 for (m = m_min; m <= m_max; m++) {
128
- _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
129
- _d_max = min(d_max, fin * m / fvco_min);
136
+ _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max_fract));
137
+ _d_max = min(d_max, fin * m / fvco_min_fract);
130138
131139 for (d = _d_min; d <= _d_max; d++) {
132140 fvco = fin * m / d;
133141
134142 dout = DIV_ROUND_CLOSEST(fvco, fout);
135
- dout = clamp_t(unsigned long, dout, 1, 128);
143
+ dout = clamp_t(unsigned long, dout, 1, 128 << fract_shift);
136144 f = fvco / dout;
137145 if (abs(f - fout) < abs(best_f - fout)) {
138146 best_f = f;
139147 *best_d = d;
140
- *best_m = m;
141
- *best_dout = dout;
148
+ *best_m = m << (3 - fract_shift);
149
+ *best_dout = dout << (3 - fract_shift);
142150 if (best_f == fout)
143151 return;
144152 }
145153 }
146154 }
155
+
156
+ /* Lets see if we find a better setting in fractional mode */
157
+ if (fract_shift == 0) {
158
+ fract_shift = 3;
159
+ goto again;
160
+ }
147161 }
148162
149
-static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
150
- unsigned int *high, unsigned int *edge, unsigned int *nocount)
151
-{
152
- if (divider == 1)
153
- *nocount = 1;
154
- else
155
- *nocount = 0;
163
+struct axi_clkgen_div_params {
164
+ unsigned int low;
165
+ unsigned int high;
166
+ unsigned int edge;
167
+ unsigned int nocount;
168
+ unsigned int frac_en;
169
+ unsigned int frac;
170
+ unsigned int frac_wf_f;
171
+ unsigned int frac_wf_r;
172
+ unsigned int frac_phase;
173
+};
156174
157
- *high = divider / 2;
158
- *edge = divider % 2;
159
- *low = divider - *high;
175
+static void axi_clkgen_calc_clk_params(unsigned int divider,
176
+ unsigned int frac_divider, struct axi_clkgen_div_params *params)
177
+{
178
+
179
+ memset(params, 0x0, sizeof(*params));
180
+
181
+ if (divider == 1) {
182
+ params->nocount = 1;
183
+ return;
184
+ }
185
+
186
+ if (frac_divider == 0) {
187
+ params->high = divider / 2;
188
+ params->edge = divider % 2;
189
+ params->low = divider - params->high;
190
+ } else {
191
+ params->frac_en = 1;
192
+ params->frac = frac_divider;
193
+
194
+ params->high = divider / 2;
195
+ params->edge = divider % 2;
196
+ params->low = params->high;
197
+
198
+ if (params->edge == 0) {
199
+ params->high--;
200
+ params->frac_wf_r = 1;
201
+ }
202
+
203
+ if (params->edge == 0 || frac_divider == 1)
204
+ params->low--;
205
+ if (((params->edge == 0) ^ (frac_divider == 1)) ||
206
+ (divider == 2 && frac_divider == 1))
207
+ params->frac_wf_f = 1;
208
+
209
+ params->frac_phase = params->edge * 4 + frac_divider / 2;
210
+ }
160211 }
161212
162213 static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
....@@ -248,15 +299,29 @@
248299 return container_of(clk_hw, struct axi_clkgen, clk_hw);
249300 }
250301
302
+static void axi_clkgen_set_div(struct axi_clkgen *axi_clkgen,
303
+ unsigned int reg1, unsigned int reg2, unsigned int reg3,
304
+ struct axi_clkgen_div_params *params)
305
+{
306
+ axi_clkgen_mmcm_write(axi_clkgen, reg1,
307
+ (params->high << 6) | params->low, 0xefff);
308
+ axi_clkgen_mmcm_write(axi_clkgen, reg2,
309
+ (params->frac << 12) | (params->frac_en << 11) |
310
+ (params->frac_wf_r << 10) | (params->edge << 7) |
311
+ (params->nocount << 6), 0x7fff);
312
+ if (reg3 != 0) {
313
+ axi_clkgen_mmcm_write(axi_clkgen, reg3,
314
+ (params->frac_phase << 11) | (params->frac_wf_f << 10), 0x3c00);
315
+ }
316
+}
317
+
251318 static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
252319 unsigned long rate, unsigned long parent_rate)
253320 {
254321 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
255322 unsigned int d, m, dout;
256
- unsigned int nocount;
257
- unsigned int high;
258
- unsigned int edge;
259
- unsigned int low;
323
+ struct axi_clkgen_div_params params;
324
+ uint32_t power = 0;
260325 uint32_t filter;
261326 uint32_t lock;
262327
....@@ -268,24 +333,26 @@
268333 if (d == 0 || dout == 0 || m == 0)
269334 return -EINVAL;
270335
336
+ if ((dout & 0x7) != 0 || (m & 0x7) != 0)
337
+ power |= 0x9800;
338
+
339
+ axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_POWER, power, 0x9800);
340
+
271341 filter = axi_clkgen_lookup_filter(m - 1);
272342 lock = axi_clkgen_lookup_lock(m - 1);
273343
274
- axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
275
- axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
276
- (high << 6) | low, 0xefff);
277
- axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
278
- (edge << 7) | (nocount << 6), 0x03ff);
344
+ axi_clkgen_calc_clk_params(dout >> 3, dout & 0x7, &params);
345
+ axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLKOUT0_1, MMCM_REG_CLKOUT0_2,
346
+ MMCM_REG_CLKOUT5_2, &params);
279347
280
- axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
348
+ axi_clkgen_calc_clk_params(d, 0, &params);
281349 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
282
- (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
350
+ (params.edge << 13) | (params.nocount << 12) |
351
+ (params.high << 6) | params.low, 0x3fff);
283352
284
- axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
285
- axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
286
- (high << 6) | low, 0xefff);
287
- axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
288
- (edge << 7) | (nocount << 6), 0x03ff);
353
+ axi_clkgen_calc_clk_params(m >> 3, m & 0x7, &params);
354
+ axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLK_FB1, MMCM_REG_CLK_FB2,
355
+ MMCM_REG_CLKOUT6_2, &params);
289356
290357 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
291358 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
....@@ -315,35 +382,51 @@
315382 return min_t(unsigned long long, tmp, LONG_MAX);
316383 }
317384
385
+static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen,
386
+ unsigned int reg1, unsigned int reg2)
387
+{
388
+ unsigned int val1, val2;
389
+ unsigned int div;
390
+
391
+ axi_clkgen_mmcm_read(axi_clkgen, reg2, &val2);
392
+ if (val2 & MMCM_CLKOUT_NOCOUNT)
393
+ return 8;
394
+
395
+ axi_clkgen_mmcm_read(axi_clkgen, reg1, &val1);
396
+
397
+ div = (val1 & 0x3f) + ((val1 >> 6) & 0x3f);
398
+ div <<= 3;
399
+
400
+ if (val2 & MMCM_CLK_DIV_DIVIDE) {
401
+ if ((val2 & BIT(7)) && (val2 & 0x7000) != 0x1000)
402
+ div += 8;
403
+ else
404
+ div += 16;
405
+
406
+ div += (val2 >> 12) & 0x7;
407
+ }
408
+
409
+ return div;
410
+}
411
+
318412 static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
319413 unsigned long parent_rate)
320414 {
321415 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
322416 unsigned int d, m, dout;
323
- unsigned int reg;
324417 unsigned long long tmp;
418
+ unsigned int val;
325419
326
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_2, &reg);
327
- if (reg & MMCM_CLKOUT_NOCOUNT) {
328
- dout = 1;
329
- } else {
330
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
331
- dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
332
- }
420
+ dout = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLKOUT0_1,
421
+ MMCM_REG_CLKOUT0_2);
422
+ m = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLK_FB1,
423
+ MMCM_REG_CLK_FB2);
333424
334
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
335
- if (reg & MMCM_CLK_DIV_NOCOUNT)
425
+ axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &val);
426
+ if (val & MMCM_CLK_DIV_NOCOUNT)
336427 d = 1;
337428 else
338
- d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
339
-
340
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB2, &reg);
341
- if (reg & MMCM_CLKOUT_NOCOUNT) {
342
- m = 1;
343
- } else {
344
- axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
345
- m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
346
- }
429
+ d = (val & 0x3f) + ((val >> 6) & 0x3f);
347430
348431 if (d == 0 || dout == 0)
349432 return 0;
....@@ -411,7 +494,7 @@
411494 {
412495 const struct of_device_id *id;
413496 struct axi_clkgen *axi_clkgen;
414
- struct clk_init_data init = {};
497
+ struct clk_init_data init;
415498 const char *parent_names[2];
416499 const char *clk_name;
417500 struct resource *mem;