• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
55 struct axi_clkgen;
56 
57 struct 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 
65 struct axi_clkgen {
66 	void __iomem *base;
67 	const struct axi_clkgen_mmcm_ops *mmcm_ops;
68 	struct clk_hw clk_hw;
69 };
70 
axi_clkgen_mmcm_enable(struct axi_clkgen * axi_clkgen,bool enable)71 static 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 
axi_clkgen_mmcm_write(struct axi_clkgen * axi_clkgen,unsigned int reg,unsigned int val,unsigned int mask)77 static 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 
axi_clkgen_mmcm_read(struct axi_clkgen * axi_clkgen,unsigned int reg,unsigned int * val)83 static 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 
axi_clkgen_lookup_filter(unsigned int m)89 static 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 
119 static 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 
axi_clkgen_lookup_lock(unsigned int m)131 static 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 
138 static const unsigned int fpfd_min = 10000;
139 static const unsigned int fpfd_max = 300000;
140 static const unsigned int fvco_min = 600000;
141 static const unsigned int fvco_max = 1200000;
142 
axi_clkgen_calc_params(unsigned long fin,unsigned long fout,unsigned int * best_d,unsigned int * best_m,unsigned int * best_dout)143 static 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 
axi_clkgen_calc_clk_params(unsigned int divider,unsigned int * low,unsigned int * high,unsigned int * edge,unsigned int * nocount)186 static 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 
axi_clkgen_write(struct axi_clkgen * axi_clkgen,unsigned int reg,unsigned int val)199 static 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 
axi_clkgen_read(struct axi_clkgen * axi_clkgen,unsigned int reg,unsigned int * val)205 static 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 
axi_clkgen_v1_map_mmcm_reg(unsigned int reg)211 static 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 
axi_clkgen_v1_mmcm_write(struct axi_clkgen * axi_clkgen,unsigned int reg,unsigned int val,unsigned int mask)239 static 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 
axi_clkgen_v1_mmcm_read(struct axi_clkgen * axi_clkgen,unsigned int reg,unsigned int * val)251 static 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 
axi_clkgen_v1_mmcm_enable(struct axi_clkgen * axi_clkgen,bool enable)263 static 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 
269 static 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 
axi_clkgen_wait_non_busy(struct axi_clkgen * axi_clkgen)275 static 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 
axi_clkgen_v2_mmcm_read(struct axi_clkgen * axi_clkgen,unsigned int reg,unsigned int * val)290 static 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 
axi_clkgen_v2_mmcm_write(struct axi_clkgen * axi_clkgen,unsigned int reg,unsigned int val,unsigned int mask)314 static 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, &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 
axi_clkgen_v2_mmcm_enable(struct axi_clkgen * axi_clkgen,bool enable)336 static 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 
347 static 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 
clk_hw_to_axi_clkgen(struct clk_hw * clk_hw)353 static 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 
axi_clkgen_set_rate(struct clk_hw * clk_hw,unsigned long rate,unsigned long parent_rate)358 static 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 
axi_clkgen_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)408 static 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 
axi_clkgen_recalc_rate(struct clk_hw * clk_hw,unsigned long parent_rate)421 static 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, &reg);
430 	dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
431 	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
432 	d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
433 	axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
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 
axi_clkgen_enable(struct clk_hw * clk_hw)448 static 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 
axi_clkgen_disable(struct clk_hw * clk_hw)457 static 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 
464 static 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 
472 static 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 };
482 MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
483 
axi_clkgen_probe(struct platform_device * pdev)484 static 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 
axi_clkgen_remove(struct platform_device * pdev)537 static int axi_clkgen_remove(struct platform_device *pdev)
538 {
539 	of_clk_del_provider(pdev->dev.of_node);
540 
541 	return 0;
542 }
543 
544 static struct platform_driver axi_clkgen_driver = {
545 	.driver = {
546 		.name = "adi-axi-clkgen",
547 		.of_match_table = axi_clkgen_ids,
548 	},
549 	.probe = axi_clkgen_probe,
550 	.remove = axi_clkgen_remove,
551 };
552 module_platform_driver(axi_clkgen_driver);
553 
554 MODULE_LICENSE("GPL v2");
555 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
556 MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");
557