File Index
Files and directories
Sign in
FILE

clk-axi-clkgen.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/clk-axi-clkgen.c C
  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/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
 56struct 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
 63struct axi_clkgen {
 64	void __iomem *base;
 65	struct clk_hw clk_hw;
 66	struct axi_clkgen_limits limits;
 67};
 68
 69static 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
 99static 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
111static 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
118static 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
125static 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
132static 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
154again:
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
189struct 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
201static 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
239static 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
245static 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
251static 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
266static 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
290static 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
313static 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
323static 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
328static 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
346static 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
395static 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
416static 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
443static 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
471static 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
480static 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
487static 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
496static 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
506static 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
554static 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
564static 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
645static 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};
656MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
657
658static 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};
665module_platform_driver(axi_clkgen_driver);
666
667MODULE_LICENSE("GPL v2");
668MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
669MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");
~/ Read-only file preview.