Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * AXI clkgen driver
4 *
5 * Copyright 2012-2013 Analog Devices Inc.
6 * Author: Lars-Peter Clausen <lars@metafoo.de>
7 */
8
9#include <linux/platform_device.h>
10#include <linux/clk.h>
11#include <linux/clk-provider.h>
12#include <linux/slab.h>
13#include <linux/io.h>
14#include <linux/of.h>
15#include <linux/module.h>
16#include <linux/err.h>
17
18#define AXI_CLKGEN_V2_REG_RESET 0x40
19#define AXI_CLKGEN_V2_REG_CLKSEL 0x44
20#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70
21#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74
22
23#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1)
24#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0)
25
26#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29)
27#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28)
28
29#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
30
31#define MMCM_REG_CLKOUT5_2 0x07
32#define MMCM_REG_CLKOUT0_1 0x08
33#define MMCM_REG_CLKOUT0_2 0x09
34#define MMCM_REG_CLKOUT6_2 0x13
35#define MMCM_REG_CLK_FB1 0x14
36#define MMCM_REG_CLK_FB2 0x15
37#define MMCM_REG_CLK_DIV 0x16
38#define MMCM_REG_LOCK1 0x18
39#define MMCM_REG_LOCK2 0x19
40#define MMCM_REG_LOCK3 0x1a
41#define MMCM_REG_POWER 0x28
42#define MMCM_REG_FILTER1 0x4e
43#define MMCM_REG_FILTER2 0x4f
44
45#define MMCM_CLKOUT_NOCOUNT BIT(6)
46
47#define MMCM_CLK_DIV_DIVIDE BIT(11)
48#define MMCM_CLK_DIV_NOCOUNT BIT(12)
49
50struct axi_clkgen_limits {
51 unsigned int fpfd_min;
52 unsigned int fpfd_max;
53 unsigned int fvco_min;
54 unsigned int fvco_max;
55};
56
57struct axi_clkgen {
58 void __iomem *base;
59 struct clk_hw clk_hw;
60 struct axi_clkgen_limits limits;
61};
62
63static uint32_t axi_clkgen_lookup_filter(unsigned int m)
64{
65 switch (m) {
66 case 0:
67 return 0x01001990;
68 case 1:
69 return 0x01001190;
70 case 2:
71 return 0x01009890;
72 case 3:
73 return 0x01001890;
74 case 4:
75 return 0x01008890;
76 case 5 ... 8:
77 return 0x01009090;
78 case 9 ... 11:
79 return 0x01000890;
80 case 12:
81 return 0x08009090;
82 case 13 ... 22:
83 return 0x01001090;
84 case 23 ... 36:
85 return 0x01008090;
86 case 37 ... 46:
87 return 0x08001090;
88 default:
89 return 0x08008090;
90 }
91}
92
93static const uint32_t axi_clkgen_lock_table[] = {
94 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
95 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
96 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
97 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
98 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
99 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
100 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
101 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
102 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
103};
104
105static uint32_t axi_clkgen_lookup_lock(unsigned int m)
106{
107 if (m < ARRAY_SIZE(axi_clkgen_lock_table))
108 return axi_clkgen_lock_table[m];
109 return 0x1f1f00fa;
110}
111
112static const struct axi_clkgen_limits axi_clkgen_zynqmp_default_limits = {
113 .fpfd_min = 10000,
114 .fpfd_max = 450000,
115 .fvco_min = 800000,
116 .fvco_max = 1600000,
117};
118
119static const struct axi_clkgen_limits axi_clkgen_zynq_default_limits = {
120 .fpfd_min = 10000,
121 .fpfd_max = 300000,
122 .fvco_min = 600000,
123 .fvco_max = 1200000,
124};
125
126static void axi_clkgen_calc_params(const struct axi_clkgen_limits *limits,
127 unsigned long fin, unsigned long fout,
128 unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
129{
130 unsigned long d, d_min, d_max, _d_min, _d_max;
131 unsigned long m, m_min, m_max;
132 unsigned long f, dout, best_f, fvco;
133 unsigned long fract_shift = 0;
134 unsigned long fvco_min_fract, fvco_max_fract;
135
136 fin /= 1000;
137 fout /= 1000;
138
139 best_f = ULONG_MAX;
140 *best_d = 0;
141 *best_m = 0;
142 *best_dout = 0;
143
144 d_min = max_t(unsigned long, DIV_ROUND_UP(fin, limits->fpfd_max), 1);
145 d_max = min_t(unsigned long, fin / limits->fpfd_min, 80);
146
147again:
148 fvco_min_fract = limits->fvco_min << fract_shift;
149 fvco_max_fract = limits->fvco_max << fract_shift;
150
151 m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1);
152 m_max = min_t(unsigned long, fvco_max_fract * d_max / fin, 64 << fract_shift);
153
154 for (m = m_min; m <= m_max; m++) {
155 _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max_fract));
156 _d_max = min(d_max, fin * m / fvco_min_fract);
157
158 for (d = _d_min; d <= _d_max; d++) {
159 fvco = fin * m / d;
160
161 dout = DIV_ROUND_CLOSEST(fvco, fout);
162 dout = clamp_t(unsigned long, dout, 1, 128 << fract_shift);
163 f = fvco / dout;
164 if (abs(f - fout) < abs(best_f - fout)) {
165 best_f = f;
166 *best_d = d;
167 *best_m = m << (3 - fract_shift);
168 *best_dout = dout << (3 - fract_shift);
169 if (best_f == fout)
170 return;
171 }
172 }
173 }
174
175 /* Lets see if we find a better setting in fractional mode */
176 if (fract_shift == 0) {
177 fract_shift = 3;
178 goto again;
179 }
180}
181
182struct axi_clkgen_div_params {
183 unsigned int low;
184 unsigned int high;
185 unsigned int edge;
186 unsigned int nocount;
187 unsigned int frac_en;
188 unsigned int frac;
189 unsigned int frac_wf_f;
190 unsigned int frac_wf_r;
191 unsigned int frac_phase;
192};
193
194static void axi_clkgen_calc_clk_params(unsigned int divider,
195 unsigned int frac_divider, struct axi_clkgen_div_params *params)
196{
197
198 memset(params, 0x0, sizeof(*params));
199
200 if (divider == 1) {
201 params->nocount = 1;
202 return;
203 }
204
205 if (frac_divider == 0) {
206 params->high = divider / 2;
207 params->edge = divider % 2;
208 params->low = divider - params->high;
209 } else {
210 params->frac_en = 1;
211 params->frac = frac_divider;
212
213 params->high = divider / 2;
214 params->edge = divider % 2;
215 params->low = params->high;
216
217 if (params->edge == 0) {
218 params->high--;
219 params->frac_wf_r = 1;
220 }
221
222 if (params->edge == 0 || frac_divider == 1)
223 params->low--;
224 if (((params->edge == 0) ^ (frac_divider == 1)) ||
225 (divider == 2 && frac_divider == 1))
226 params->frac_wf_f = 1;
227
228 params->frac_phase = params->edge * 4 + frac_divider / 2;
229 }
230}
231
232static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
233 unsigned int reg, unsigned int val)
234{
235 writel(val, axi_clkgen->base + reg);
236}
237
238static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
239 unsigned int reg, unsigned int *val)
240{
241 *val = readl(axi_clkgen->base + reg);
242}
243
244static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
245{
246 unsigned int timeout = 10000;
247 unsigned int val;
248
249 do {
250 axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
251 } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
252
253 if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
254 return -EIO;
255
256 return val & 0xffff;
257}
258
259static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
260 unsigned int reg, unsigned int *val)
261{
262 unsigned int reg_val;
263 int ret;
264
265 ret = axi_clkgen_wait_non_busy(axi_clkgen);
266 if (ret < 0)
267 return ret;
268
269 reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
270 reg_val |= (reg << 16);
271
272 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
273
274 ret = axi_clkgen_wait_non_busy(axi_clkgen);
275 if (ret < 0)
276 return ret;
277
278 *val = ret;
279
280 return 0;
281}
282
283static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
284 unsigned int reg, unsigned int val, unsigned int mask)
285{
286 unsigned int reg_val = 0;
287 int ret;
288
289 ret = axi_clkgen_wait_non_busy(axi_clkgen);
290 if (ret < 0)
291 return ret;
292
293 if (mask != 0xffff) {
294 axi_clkgen_mmcm_read(axi_clkgen, reg, ®_val);
295 reg_val &= ~mask;
296 }
297
298 reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
299
300 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
301
302 return 0;
303}
304
305static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
306 bool enable)
307{
308 unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
309
310 if (enable)
311 val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
312
313 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
314}
315
316static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
317{
318 return container_of(clk_hw, struct axi_clkgen, clk_hw);
319}
320
321static void axi_clkgen_set_div(struct axi_clkgen *axi_clkgen,
322 unsigned int reg1, unsigned int reg2, unsigned int reg3,
323 struct axi_clkgen_div_params *params)
324{
325 axi_clkgen_mmcm_write(axi_clkgen, reg1,
326 (params->high << 6) | params->low, 0xefff);
327 axi_clkgen_mmcm_write(axi_clkgen, reg2,
328 (params->frac << 12) | (params->frac_en << 11) |
329 (params->frac_wf_r << 10) | (params->edge << 7) |
330 (params->nocount << 6), 0x7fff);
331 if (reg3 != 0) {
332 axi_clkgen_mmcm_write(axi_clkgen, reg3,
333 (params->frac_phase << 11) | (params->frac_wf_f << 10), 0x3c00);
334 }
335}
336
337static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
338 unsigned long rate, unsigned long parent_rate)
339{
340 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
341 const struct axi_clkgen_limits *limits = &axi_clkgen->limits;
342 unsigned int d, m, dout;
343 struct axi_clkgen_div_params params;
344 uint32_t power = 0;
345 uint32_t filter;
346 uint32_t lock;
347
348 if (parent_rate == 0 || rate == 0)
349 return -EINVAL;
350
351 axi_clkgen_calc_params(limits, parent_rate, rate, &d, &m, &dout);
352
353 if (d == 0 || dout == 0 || m == 0)
354 return -EINVAL;
355
356 if ((dout & 0x7) != 0 || (m & 0x7) != 0)
357 power |= 0x9800;
358
359 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_POWER, power, 0x9800);
360
361 filter = axi_clkgen_lookup_filter(m - 1);
362 lock = axi_clkgen_lookup_lock(m - 1);
363
364 axi_clkgen_calc_clk_params(dout >> 3, dout & 0x7, ¶ms);
365 axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLKOUT0_1, MMCM_REG_CLKOUT0_2,
366 MMCM_REG_CLKOUT5_2, ¶ms);
367
368 axi_clkgen_calc_clk_params(d, 0, ¶ms);
369 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
370 (params.edge << 13) | (params.nocount << 12) |
371 (params.high << 6) | params.low, 0x3fff);
372
373 axi_clkgen_calc_clk_params(m >> 3, m & 0x7, ¶ms);
374 axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLK_FB1, MMCM_REG_CLK_FB2,
375 MMCM_REG_CLKOUT6_2, ¶ms);
376
377 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
378 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
379 (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
380 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
381 (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
382 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
383 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
384
385 return 0;
386}
387
388static int axi_clkgen_determine_rate(struct clk_hw *hw,
389 struct clk_rate_request *req)
390{
391 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(hw);
392 const struct axi_clkgen_limits *limits = &axi_clkgen->limits;
393 unsigned int d, m, dout;
394 unsigned long long tmp;
395
396 axi_clkgen_calc_params(limits, req->best_parent_rate, req->rate,
397 &d, &m, &dout);
398
399 if (d == 0 || dout == 0 || m == 0)
400 return -EINVAL;
401
402 tmp = (unsigned long long)req->best_parent_rate * m;
403 tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d);
404
405 req->rate = min_t(unsigned long long, tmp, LONG_MAX);
406 return 0;
407}
408
409static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen,
410 unsigned int reg1, unsigned int reg2)
411{
412 unsigned int val1, val2;
413 unsigned int div;
414
415 axi_clkgen_mmcm_read(axi_clkgen, reg2, &val2);
416 if (val2 & MMCM_CLKOUT_NOCOUNT)
417 return 8;
418
419 axi_clkgen_mmcm_read(axi_clkgen, reg1, &val1);
420
421 div = (val1 & 0x3f) + ((val1 >> 6) & 0x3f);
422 div <<= 3;
423
424 if (val2 & MMCM_CLK_DIV_DIVIDE) {
425 if ((val2 & BIT(7)) && (val2 & 0x7000) != 0x1000)
426 div += 8;
427 else
428 div += 16;
429
430 div += (val2 >> 12) & 0x7;
431 }
432
433 return div;
434}
435
436static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
437 unsigned long parent_rate)
438{
439 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
440 unsigned int d, m, dout;
441 unsigned long long tmp;
442 unsigned int val;
443
444 dout = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLKOUT0_1,
445 MMCM_REG_CLKOUT0_2);
446 m = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLK_FB1,
447 MMCM_REG_CLK_FB2);
448
449 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &val);
450 if (val & MMCM_CLK_DIV_NOCOUNT)
451 d = 1;
452 else
453 d = (val & 0x3f) + ((val >> 6) & 0x3f);
454
455 if (d == 0 || dout == 0)
456 return 0;
457
458 tmp = (unsigned long long)parent_rate * m;
459 tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d);
460
461 return min_t(unsigned long long, tmp, ULONG_MAX);
462}
463
464static int axi_clkgen_enable(struct clk_hw *clk_hw)
465{
466 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
467
468 axi_clkgen_mmcm_enable(axi_clkgen, true);
469
470 return 0;
471}
472
473static void axi_clkgen_disable(struct clk_hw *clk_hw)
474{
475 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
476
477 axi_clkgen_mmcm_enable(axi_clkgen, false);
478}
479
480static int axi_clkgen_set_parent(struct clk_hw *clk_hw, u8 index)
481{
482 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
483
484 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_CLKSEL, index);
485
486 return 0;
487}
488
489static u8 axi_clkgen_get_parent(struct clk_hw *clk_hw)
490{
491 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
492 unsigned int parent;
493
494 axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_CLKSEL, &parent);
495
496 return parent;
497}
498
499static const struct clk_ops axi_clkgen_ops = {
500 .recalc_rate = axi_clkgen_recalc_rate,
501 .determine_rate = axi_clkgen_determine_rate,
502 .set_rate = axi_clkgen_set_rate,
503 .enable = axi_clkgen_enable,
504 .disable = axi_clkgen_disable,
505 .set_parent = axi_clkgen_set_parent,
506 .get_parent = axi_clkgen_get_parent,
507};
508
509static int axi_clkgen_probe(struct platform_device *pdev)
510{
511 const struct axi_clkgen_limits *dflt_limits;
512 struct axi_clkgen *axi_clkgen;
513 struct clk_init_data init;
514 const char *parent_names[2];
515 const char *clk_name;
516 struct clk *axi_clk;
517 unsigned int i;
518 int ret;
519
520 dflt_limits = device_get_match_data(&pdev->dev);
521 if (!dflt_limits)
522 return -ENODEV;
523
524 axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
525 if (!axi_clkgen)
526 return -ENOMEM;
527
528 axi_clkgen->base = devm_platform_ioremap_resource(pdev, 0);
529 if (IS_ERR(axi_clkgen->base))
530 return PTR_ERR(axi_clkgen->base);
531
532 init.num_parents = of_clk_get_parent_count(pdev->dev.of_node);
533
534 axi_clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
535 if (!IS_ERR(axi_clk)) {
536 if (init.num_parents < 2 || init.num_parents > 3)
537 return -EINVAL;
538
539 init.num_parents -= 1;
540 } else {
541 /*
542 * Legacy... So that old DTs which do not have clock-names still
543 * work. In this case we don't explicitly enable the AXI bus
544 * clock.
545 */
546 if (PTR_ERR(axi_clk) != -ENOENT)
547 return PTR_ERR(axi_clk);
548 if (init.num_parents < 1 || init.num_parents > 2)
549 return -EINVAL;
550 }
551
552 for (i = 0; i < init.num_parents; i++) {
553 parent_names[i] = of_clk_get_parent_name(pdev->dev.of_node, i);
554 if (!parent_names[i])
555 return -EINVAL;
556 }
557
558 memcpy(&axi_clkgen->limits, dflt_limits, sizeof(axi_clkgen->limits));
559
560 clk_name = pdev->dev.of_node->name;
561 of_property_read_string(pdev->dev.of_node, "clock-output-names",
562 &clk_name);
563
564 init.name = clk_name;
565 init.ops = &axi_clkgen_ops;
566 init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
567 init.parent_names = parent_names;
568
569 axi_clkgen_mmcm_enable(axi_clkgen, false);
570
571 axi_clkgen->clk_hw.init = &init;
572 ret = devm_clk_hw_register(&pdev->dev, &axi_clkgen->clk_hw);
573 if (ret)
574 return ret;
575
576 return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
577 &axi_clkgen->clk_hw);
578}
579
580static const struct of_device_id axi_clkgen_ids[] = {
581 {
582 .compatible = "adi,zynqmp-axi-clkgen-2.00.a",
583 .data = &axi_clkgen_zynqmp_default_limits,
584 },
585 {
586 .compatible = "adi,axi-clkgen-2.00.a",
587 .data = &axi_clkgen_zynq_default_limits,
588 },
589 { }
590};
591MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
592
593static struct platform_driver axi_clkgen_driver = {
594 .driver = {
595 .name = "adi-axi-clkgen",
596 .of_match_table = axi_clkgen_ids,
597 },
598 .probe = axi_clkgen_probe,
599};
600module_platform_driver(axi_clkgen_driver);
601
602MODULE_LICENSE("GPL v2");
603MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
604MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");
1/*
2 * AXI clkgen driver
3 *
4 * Copyright 2012-2013 Analog Devices Inc.
5 * Author: Lars-Peter Clausen <lars@metafoo.de>
6 *
7 * Licensed under the GPL-2.
8 *
9 */
10
11#include <linux/platform_device.h>
12#include <linux/clk-provider.h>
13#include <linux/clk.h>
14#include <linux/slab.h>
15#include <linux/io.h>
16#include <linux/of.h>
17#include <linux/module.h>
18#include <linux/err.h>
19
20#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04
21#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08
22#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c
23#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10
24#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14
25#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18
26#define AXI_CLKGEN_V1_REG_LOCK1 0x1c
27#define AXI_CLKGEN_V1_REG_LOCK2 0x20
28#define AXI_CLKGEN_V1_REG_LOCK3 0x24
29#define AXI_CLKGEN_V1_REG_FILTER1 0x28
30#define AXI_CLKGEN_V1_REG_FILTER2 0x2c
31
32#define AXI_CLKGEN_V2_REG_RESET 0x40
33#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70
34#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74
35
36#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1)
37#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0)
38
39#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29)
40#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28)
41
42#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
43
44#define MMCM_REG_CLKOUT0_1 0x08
45#define MMCM_REG_CLKOUT0_2 0x09
46#define MMCM_REG_CLK_FB1 0x14
47#define MMCM_REG_CLK_FB2 0x15
48#define MMCM_REG_CLK_DIV 0x16
49#define MMCM_REG_LOCK1 0x18
50#define MMCM_REG_LOCK2 0x19
51#define MMCM_REG_LOCK3 0x1a
52#define MMCM_REG_FILTER1 0x4e
53#define MMCM_REG_FILTER2 0x4f
54
55struct axi_clkgen;
56
57struct axi_clkgen_mmcm_ops {
58 void (*enable)(struct axi_clkgen *axi_clkgen, bool enable);
59 int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg,
60 unsigned int val, unsigned int mask);
61 int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg,
62 unsigned int *val);
63};
64
65struct axi_clkgen {
66 void __iomem *base;
67 const struct axi_clkgen_mmcm_ops *mmcm_ops;
68 struct clk_hw clk_hw;
69};
70
71static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
72 bool enable)
73{
74 axi_clkgen->mmcm_ops->enable(axi_clkgen, enable);
75}
76
77static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
78 unsigned int reg, unsigned int val, unsigned int mask)
79{
80 return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask);
81}
82
83static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
84 unsigned int reg, unsigned int *val)
85{
86 return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val);
87}
88
89static uint32_t axi_clkgen_lookup_filter(unsigned int m)
90{
91 switch (m) {
92 case 0:
93 return 0x01001990;
94 case 1:
95 return 0x01001190;
96 case 2:
97 return 0x01009890;
98 case 3:
99 return 0x01001890;
100 case 4:
101 return 0x01008890;
102 case 5 ... 8:
103 return 0x01009090;
104 case 9 ... 11:
105 return 0x01000890;
106 case 12:
107 return 0x08009090;
108 case 13 ... 22:
109 return 0x01001090;
110 case 23 ... 36:
111 return 0x01008090;
112 case 37 ... 46:
113 return 0x08001090;
114 default:
115 return 0x08008090;
116 }
117}
118
119static const uint32_t axi_clkgen_lock_table[] = {
120 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
121 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
122 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
123 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
124 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
125 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
126 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
127 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
128 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
129};
130
131static uint32_t axi_clkgen_lookup_lock(unsigned int m)
132{
133 if (m < ARRAY_SIZE(axi_clkgen_lock_table))
134 return axi_clkgen_lock_table[m];
135 return 0x1f1f00fa;
136}
137
138static const unsigned int fpfd_min = 10000;
139static const unsigned int fpfd_max = 300000;
140static const unsigned int fvco_min = 600000;
141static const unsigned int fvco_max = 1200000;
142
143static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
144 unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
145{
146 unsigned long d, d_min, d_max, _d_min, _d_max;
147 unsigned long m, m_min, m_max;
148 unsigned long f, dout, best_f, fvco;
149
150 fin /= 1000;
151 fout /= 1000;
152
153 best_f = ULONG_MAX;
154 *best_d = 0;
155 *best_m = 0;
156 *best_dout = 0;
157
158 d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
159 d_max = min_t(unsigned long, fin / fpfd_min, 80);
160
161 m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
162 m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
163
164 for (m = m_min; m <= m_max; m++) {
165 _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
166 _d_max = min(d_max, fin * m / fvco_min);
167
168 for (d = _d_min; d <= _d_max; d++) {
169 fvco = fin * m / d;
170
171 dout = DIV_ROUND_CLOSEST(fvco, fout);
172 dout = clamp_t(unsigned long, dout, 1, 128);
173 f = fvco / dout;
174 if (abs(f - fout) < abs(best_f - fout)) {
175 best_f = f;
176 *best_d = d;
177 *best_m = m;
178 *best_dout = dout;
179 if (best_f == fout)
180 return;
181 }
182 }
183 }
184}
185
186static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
187 unsigned int *high, unsigned int *edge, unsigned int *nocount)
188{
189 if (divider == 1)
190 *nocount = 1;
191 else
192 *nocount = 0;
193
194 *high = divider / 2;
195 *edge = divider % 2;
196 *low = divider - *high;
197}
198
199static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
200 unsigned int reg, unsigned int val)
201{
202 writel(val, axi_clkgen->base + reg);
203}
204
205static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
206 unsigned int reg, unsigned int *val)
207{
208 *val = readl(axi_clkgen->base + reg);
209}
210
211static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg)
212{
213 switch (reg) {
214 case MMCM_REG_CLKOUT0_1:
215 return AXI_CLKGEN_V1_REG_CLK_OUT1;
216 case MMCM_REG_CLKOUT0_2:
217 return AXI_CLKGEN_V1_REG_CLK_OUT2;
218 case MMCM_REG_CLK_FB1:
219 return AXI_CLKGEN_V1_REG_CLK_FB1;
220 case MMCM_REG_CLK_FB2:
221 return AXI_CLKGEN_V1_REG_CLK_FB2;
222 case MMCM_REG_CLK_DIV:
223 return AXI_CLKGEN_V1_REG_CLK_DIV;
224 case MMCM_REG_LOCK1:
225 return AXI_CLKGEN_V1_REG_LOCK1;
226 case MMCM_REG_LOCK2:
227 return AXI_CLKGEN_V1_REG_LOCK2;
228 case MMCM_REG_LOCK3:
229 return AXI_CLKGEN_V1_REG_LOCK3;
230 case MMCM_REG_FILTER1:
231 return AXI_CLKGEN_V1_REG_FILTER1;
232 case MMCM_REG_FILTER2:
233 return AXI_CLKGEN_V1_REG_FILTER2;
234 default:
235 return 0;
236 }
237}
238
239static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen,
240 unsigned int reg, unsigned int val, unsigned int mask)
241{
242 reg = axi_clkgen_v1_map_mmcm_reg(reg);
243 if (reg == 0)
244 return -EINVAL;
245
246 axi_clkgen_write(axi_clkgen, reg, val);
247
248 return 0;
249}
250
251static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen,
252 unsigned int reg, unsigned int *val)
253{
254 reg = axi_clkgen_v1_map_mmcm_reg(reg);
255 if (reg == 0)
256 return -EINVAL;
257
258 axi_clkgen_read(axi_clkgen, reg, val);
259
260 return 0;
261}
262
263static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen,
264 bool enable)
265{
266 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable);
267}
268
269static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = {
270 .write = axi_clkgen_v1_mmcm_write,
271 .read = axi_clkgen_v1_mmcm_read,
272 .enable = axi_clkgen_v1_mmcm_enable,
273};
274
275static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
276{
277 unsigned int timeout = 10000;
278 unsigned int val;
279
280 do {
281 axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
282 } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
283
284 if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
285 return -EIO;
286
287 return val & 0xffff;
288}
289
290static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen,
291 unsigned int reg, unsigned int *val)
292{
293 unsigned int reg_val;
294 int ret;
295
296 ret = axi_clkgen_wait_non_busy(axi_clkgen);
297 if (ret < 0)
298 return ret;
299
300 reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
301 reg_val |= (reg << 16);
302
303 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
304
305 ret = axi_clkgen_wait_non_busy(axi_clkgen);
306 if (ret < 0)
307 return ret;
308
309 *val = ret;
310
311 return 0;
312}
313
314static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen,
315 unsigned int reg, unsigned int val, unsigned int mask)
316{
317 unsigned int reg_val = 0;
318 int ret;
319
320 ret = axi_clkgen_wait_non_busy(axi_clkgen);
321 if (ret < 0)
322 return ret;
323
324 if (mask != 0xffff) {
325 axi_clkgen_v2_mmcm_read(axi_clkgen, reg, ®_val);
326 reg_val &= ~mask;
327 }
328
329 reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
330
331 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
332
333 return 0;
334}
335
336static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen,
337 bool enable)
338{
339 unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
340
341 if (enable)
342 val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
343
344 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
345}
346
347static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = {
348 .write = axi_clkgen_v2_mmcm_write,
349 .read = axi_clkgen_v2_mmcm_read,
350 .enable = axi_clkgen_v2_mmcm_enable,
351};
352
353static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
354{
355 return container_of(clk_hw, struct axi_clkgen, clk_hw);
356}
357
358static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
359 unsigned long rate, unsigned long parent_rate)
360{
361 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
362 unsigned int d, m, dout;
363 unsigned int nocount;
364 unsigned int high;
365 unsigned int edge;
366 unsigned int low;
367 uint32_t filter;
368 uint32_t lock;
369
370 if (parent_rate == 0 || rate == 0)
371 return -EINVAL;
372
373 axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
374
375 if (d == 0 || dout == 0 || m == 0)
376 return -EINVAL;
377
378 filter = axi_clkgen_lookup_filter(m - 1);
379 lock = axi_clkgen_lookup_lock(m - 1);
380
381 axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
382 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
383 (high << 6) | low, 0xefff);
384 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
385 (edge << 7) | (nocount << 6), 0x03ff);
386
387 axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
388 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
389 (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
390
391 axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
392 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
393 (high << 6) | low, 0xefff);
394 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
395 (edge << 7) | (nocount << 6), 0x03ff);
396
397 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
398 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
399 (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
400 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
401 (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
402 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
403 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
404
405 return 0;
406}
407
408static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
409 unsigned long *parent_rate)
410{
411 unsigned int d, m, dout;
412
413 axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
414
415 if (d == 0 || dout == 0 || m == 0)
416 return -EINVAL;
417
418 return *parent_rate / d * m / dout;
419}
420
421static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
422 unsigned long parent_rate)
423{
424 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
425 unsigned int d, m, dout;
426 unsigned int reg;
427 unsigned long long tmp;
428
429 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®);
430 dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
431 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®);
432 d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
433 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®);
434 m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
435
436 if (d == 0 || dout == 0)
437 return 0;
438
439 tmp = (unsigned long long)(parent_rate / d) * m;
440 do_div(tmp, dout);
441
442 if (tmp > ULONG_MAX)
443 return ULONG_MAX;
444
445 return tmp;
446}
447
448static int axi_clkgen_enable(struct clk_hw *clk_hw)
449{
450 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
451
452 axi_clkgen_mmcm_enable(axi_clkgen, true);
453
454 return 0;
455}
456
457static void axi_clkgen_disable(struct clk_hw *clk_hw)
458{
459 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
460
461 axi_clkgen_mmcm_enable(axi_clkgen, false);
462}
463
464static const struct clk_ops axi_clkgen_ops = {
465 .recalc_rate = axi_clkgen_recalc_rate,
466 .round_rate = axi_clkgen_round_rate,
467 .set_rate = axi_clkgen_set_rate,
468 .enable = axi_clkgen_enable,
469 .disable = axi_clkgen_disable,
470};
471
472static const struct of_device_id axi_clkgen_ids[] = {
473 {
474 .compatible = "adi,axi-clkgen-1.00.a",
475 .data = &axi_clkgen_v1_mmcm_ops
476 }, {
477 .compatible = "adi,axi-clkgen-2.00.a",
478 .data = &axi_clkgen_v2_mmcm_ops,
479 },
480 { },
481};
482MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
483
484static int axi_clkgen_probe(struct platform_device *pdev)
485{
486 const struct of_device_id *id;
487 struct axi_clkgen *axi_clkgen;
488 struct clk_init_data init;
489 const char *parent_name;
490 const char *clk_name;
491 struct resource *mem;
492 struct clk *clk;
493
494 if (!pdev->dev.of_node)
495 return -ENODEV;
496
497 id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
498 if (!id)
499 return -ENODEV;
500
501 axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
502 if (!axi_clkgen)
503 return -ENOMEM;
504
505 axi_clkgen->mmcm_ops = id->data;
506
507 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
508 axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
509 if (IS_ERR(axi_clkgen->base))
510 return PTR_ERR(axi_clkgen->base);
511
512 parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
513 if (!parent_name)
514 return -EINVAL;
515
516 clk_name = pdev->dev.of_node->name;
517 of_property_read_string(pdev->dev.of_node, "clock-output-names",
518 &clk_name);
519
520 init.name = clk_name;
521 init.ops = &axi_clkgen_ops;
522 init.flags = CLK_SET_RATE_GATE;
523 init.parent_names = &parent_name;
524 init.num_parents = 1;
525
526 axi_clkgen_mmcm_enable(axi_clkgen, false);
527
528 axi_clkgen->clk_hw.init = &init;
529 clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
530 if (IS_ERR(clk))
531 return PTR_ERR(clk);
532
533 return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
534 clk);
535}
536
537static int axi_clkgen_remove(struct platform_device *pdev)
538{
539 of_clk_del_provider(pdev->dev.of_node);
540
541 return 0;
542}
543
544static struct platform_driver axi_clkgen_driver = {
545 .driver = {
546 .name = "adi-axi-clkgen",
547 .owner = THIS_MODULE,
548 .of_match_table = axi_clkgen_ids,
549 },
550 .probe = axi_clkgen_probe,
551 .remove = axi_clkgen_remove,
552};
553module_platform_driver(axi_clkgen_driver);
554
555MODULE_LICENSE("GPL v2");
556MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
557MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");