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