blob: 3bcd42fbb55e3fbbcdb401f707aef3879fe5d001 [file] [log] [blame]
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +01001/*
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>
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +010013#include <linux/slab.h>
14#include <linux/io.h>
15#include <linux/of.h>
16#include <linux/module.h>
17#include <linux/err.h>
18
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +010019#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04
20#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08
21#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c
22#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10
23#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14
24#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18
25#define AXI_CLKGEN_V1_REG_LOCK1 0x1c
26#define AXI_CLKGEN_V1_REG_LOCK2 0x20
27#define AXI_CLKGEN_V1_REG_LOCK3 0x24
28#define AXI_CLKGEN_V1_REG_FILTER1 0x28
29#define AXI_CLKGEN_V1_REG_FILTER2 0x2c
30
31#define AXI_CLKGEN_V2_REG_RESET 0x40
32#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70
33#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74
34
35#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1)
36#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0)
37
38#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29)
39#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28)
40
41#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16)
42
43#define MMCM_REG_CLKOUT0_1 0x08
44#define MMCM_REG_CLKOUT0_2 0x09
45#define MMCM_REG_CLK_FB1 0x14
46#define MMCM_REG_CLK_FB2 0x15
47#define MMCM_REG_CLK_DIV 0x16
48#define MMCM_REG_LOCK1 0x18
49#define MMCM_REG_LOCK2 0x19
50#define MMCM_REG_LOCK3 0x1a
51#define MMCM_REG_FILTER1 0x4e
52#define MMCM_REG_FILTER2 0x4f
53
54struct axi_clkgen;
55
56struct axi_clkgen_mmcm_ops {
57 void (*enable)(struct axi_clkgen *axi_clkgen, bool enable);
58 int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg,
59 unsigned int val, unsigned int mask);
60 int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg,
61 unsigned int *val);
62};
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +010063
64struct axi_clkgen {
65 void __iomem *base;
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +010066 const struct axi_clkgen_mmcm_ops *mmcm_ops;
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +010067 struct clk_hw clk_hw;
68};
69
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +010070static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen,
71 bool enable)
72{
73 axi_clkgen->mmcm_ops->enable(axi_clkgen, enable);
74}
75
76static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen,
77 unsigned int reg, unsigned int val, unsigned int mask)
78{
79 return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask);
80}
81
82static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen,
83 unsigned int reg, unsigned int *val)
84{
85 return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val);
86}
87
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +010088static uint32_t axi_clkgen_lookup_filter(unsigned int m)
89{
90 switch (m) {
91 case 0:
92 return 0x01001990;
93 case 1:
94 return 0x01001190;
95 case 2:
96 return 0x01009890;
97 case 3:
98 return 0x01001890;
99 case 4:
100 return 0x01008890;
101 case 5 ... 8:
102 return 0x01009090;
103 case 9 ... 11:
104 return 0x01000890;
105 case 12:
106 return 0x08009090;
107 case 13 ... 22:
108 return 0x01001090;
109 case 23 ... 36:
110 return 0x01008090;
111 case 37 ... 46:
112 return 0x08001090;
113 default:
114 return 0x08008090;
115 }
116}
117
118static const uint32_t axi_clkgen_lock_table[] = {
119 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
120 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
121 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
122 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
123 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
124 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
125 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
126 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
127 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
128};
129
130static uint32_t axi_clkgen_lookup_lock(unsigned int m)
131{
132 if (m < ARRAY_SIZE(axi_clkgen_lock_table))
133 return axi_clkgen_lock_table[m];
134 return 0x1f1f00fa;
135}
136
137static const unsigned int fpfd_min = 10000;
138static const unsigned int fpfd_max = 300000;
139static const unsigned int fvco_min = 600000;
140static const unsigned int fvco_max = 1200000;
141
142static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
143 unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout)
144{
145 unsigned long d, d_min, d_max, _d_min, _d_max;
146 unsigned long m, m_min, m_max;
147 unsigned long f, dout, best_f, fvco;
148
149 fin /= 1000;
150 fout /= 1000;
151
152 best_f = ULONG_MAX;
153 *best_d = 0;
154 *best_m = 0;
155 *best_dout = 0;
156
157 d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
158 d_max = min_t(unsigned long, fin / fpfd_min, 80);
159
160 m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
161 m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
162
163 for (m = m_min; m <= m_max; m++) {
164 _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
165 _d_max = min(d_max, fin * m / fvco_min);
166
167 for (d = _d_min; d <= _d_max; d++) {
168 fvco = fin * m / d;
169
170 dout = DIV_ROUND_CLOSEST(fvco, fout);
171 dout = clamp_t(unsigned long, dout, 1, 128);
172 f = fvco / dout;
173 if (abs(f - fout) < abs(best_f - fout)) {
174 best_f = f;
175 *best_d = d;
176 *best_m = m;
177 *best_dout = dout;
178 if (best_f == fout)
179 return;
180 }
181 }
182 }
183}
184
185static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
186 unsigned int *high, unsigned int *edge, unsigned int *nocount)
187{
188 if (divider == 1)
189 *nocount = 1;
190 else
191 *nocount = 0;
192
193 *high = divider / 2;
194 *edge = divider % 2;
195 *low = divider - *high;
196}
197
198static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
199 unsigned int reg, unsigned int val)
200{
201 writel(val, axi_clkgen->base + reg);
202}
203
204static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
205 unsigned int reg, unsigned int *val)
206{
207 *val = readl(axi_clkgen->base + reg);
208}
209
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100210static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg)
211{
212 switch (reg) {
213 case MMCM_REG_CLKOUT0_1:
214 return AXI_CLKGEN_V1_REG_CLK_OUT1;
215 case MMCM_REG_CLKOUT0_2:
216 return AXI_CLKGEN_V1_REG_CLK_OUT2;
217 case MMCM_REG_CLK_FB1:
218 return AXI_CLKGEN_V1_REG_CLK_FB1;
219 case MMCM_REG_CLK_FB2:
220 return AXI_CLKGEN_V1_REG_CLK_FB2;
221 case MMCM_REG_CLK_DIV:
222 return AXI_CLKGEN_V1_REG_CLK_DIV;
223 case MMCM_REG_LOCK1:
224 return AXI_CLKGEN_V1_REG_LOCK1;
225 case MMCM_REG_LOCK2:
226 return AXI_CLKGEN_V1_REG_LOCK2;
227 case MMCM_REG_LOCK3:
228 return AXI_CLKGEN_V1_REG_LOCK3;
229 case MMCM_REG_FILTER1:
230 return AXI_CLKGEN_V1_REG_FILTER1;
231 case MMCM_REG_FILTER2:
232 return AXI_CLKGEN_V1_REG_FILTER2;
233 default:
234 return 0;
235 }
236}
237
238static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen,
239 unsigned int reg, unsigned int val, unsigned int mask)
240{
241 reg = axi_clkgen_v1_map_mmcm_reg(reg);
242 if (reg == 0)
243 return -EINVAL;
244
245 axi_clkgen_write(axi_clkgen, reg, val);
246
247 return 0;
248}
249
250static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen,
251 unsigned int reg, unsigned int *val)
252{
253 reg = axi_clkgen_v1_map_mmcm_reg(reg);
254 if (reg == 0)
255 return -EINVAL;
256
257 axi_clkgen_read(axi_clkgen, reg, val);
258
259 return 0;
260}
261
262static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen,
263 bool enable)
264{
265 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable);
266}
267
268static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = {
269 .write = axi_clkgen_v1_mmcm_write,
270 .read = axi_clkgen_v1_mmcm_read,
271 .enable = axi_clkgen_v1_mmcm_enable,
272};
273
274static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen)
275{
276 unsigned int timeout = 10000;
277 unsigned int val;
278
279 do {
280 axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val);
281 } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout);
282
283 if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY)
284 return -EIO;
285
286 return val & 0xffff;
287}
288
289static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen,
290 unsigned int reg, unsigned int *val)
291{
292 unsigned int reg_val;
293 int ret;
294
295 ret = axi_clkgen_wait_non_busy(axi_clkgen);
296 if (ret < 0)
297 return ret;
298
299 reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ;
300 reg_val |= (reg << 16);
301
302 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
303
304 ret = axi_clkgen_wait_non_busy(axi_clkgen);
305 if (ret < 0)
306 return ret;
307
308 *val = ret;
309
310 return 0;
311}
312
313static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen,
314 unsigned int reg, unsigned int val, unsigned int mask)
315{
316 unsigned int reg_val = 0;
317 int ret;
318
319 ret = axi_clkgen_wait_non_busy(axi_clkgen);
320 if (ret < 0)
321 return ret;
322
323 if (mask != 0xffff) {
324 axi_clkgen_v2_mmcm_read(axi_clkgen, reg, &reg_val);
325 reg_val &= ~mask;
326 }
327
328 reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask);
329
330 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val);
331
332 return 0;
333}
334
335static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen,
336 bool enable)
337{
338 unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE;
339
340 if (enable)
341 val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE;
342
343 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val);
344}
345
346static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = {
347 .write = axi_clkgen_v2_mmcm_write,
348 .read = axi_clkgen_v2_mmcm_read,
349 .enable = axi_clkgen_v2_mmcm_enable,
350};
351
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100352static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
353{
354 return container_of(clk_hw, struct axi_clkgen, clk_hw);
355}
356
357static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
358 unsigned long rate, unsigned long parent_rate)
359{
360 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
361 unsigned int d, m, dout;
362 unsigned int nocount;
363 unsigned int high;
364 unsigned int edge;
365 unsigned int low;
366 uint32_t filter;
367 uint32_t lock;
368
369 if (parent_rate == 0 || rate == 0)
370 return -EINVAL;
371
372 axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
373
374 if (d == 0 || dout == 0 || m == 0)
375 return -EINVAL;
376
377 filter = axi_clkgen_lookup_filter(m - 1);
378 lock = axi_clkgen_lookup_lock(m - 1);
379
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100380 axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100381 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
382 (high << 6) | low, 0xefff);
383 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
384 (edge << 7) | (nocount << 6), 0x03ff);
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100385
386 axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100387 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
388 (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100389
390 axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100391 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
392 (high << 6) | low, 0xefff);
393 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
394 (edge << 7) | (nocount << 6), 0x03ff);
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100395
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100396 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
397 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
398 (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
399 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
400 (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
401 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
402 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100403
404 return 0;
405}
406
407static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
408 unsigned long *parent_rate)
409{
410 unsigned int d, m, dout;
411
412 axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
413
414 if (d == 0 || dout == 0 || m == 0)
415 return -EINVAL;
416
417 return *parent_rate / d * m / dout;
418}
419
420static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
421 unsigned long parent_rate)
422{
423 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
424 unsigned int d, m, dout;
425 unsigned int reg;
426 unsigned long long tmp;
427
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100428 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100429 dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100430 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100431 d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100432 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100433 m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
434
435 if (d == 0 || dout == 0)
436 return 0;
437
438 tmp = (unsigned long long)(parent_rate / d) * m;
439 do_div(tmp, dout);
440
441 if (tmp > ULONG_MAX)
442 return ULONG_MAX;
443
444 return tmp;
445}
446
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100447static int axi_clkgen_enable(struct clk_hw *clk_hw)
448{
449 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
450
451 axi_clkgen_mmcm_enable(axi_clkgen, true);
452
453 return 0;
454}
455
456static void axi_clkgen_disable(struct clk_hw *clk_hw)
457{
458 struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
459
460 axi_clkgen_mmcm_enable(axi_clkgen, false);
461}
462
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100463static const struct clk_ops axi_clkgen_ops = {
464 .recalc_rate = axi_clkgen_recalc_rate,
465 .round_rate = axi_clkgen_round_rate,
466 .set_rate = axi_clkgen_set_rate,
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100467 .enable = axi_clkgen_enable,
468 .disable = axi_clkgen_disable,
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100469};
470
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100471static const struct of_device_id axi_clkgen_ids[] = {
472 {
473 .compatible = "adi,axi-clkgen-1.00.a",
474 .data = &axi_clkgen_v1_mmcm_ops
475 }, {
476 .compatible = "adi,axi-clkgen-2.00.a",
477 .data = &axi_clkgen_v2_mmcm_ops,
478 },
479 { },
480};
481MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
482
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100483static int axi_clkgen_probe(struct platform_device *pdev)
484{
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100485 const struct of_device_id *id;
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100486 struct axi_clkgen *axi_clkgen;
487 struct clk_init_data init;
488 const char *parent_name;
489 const char *clk_name;
490 struct resource *mem;
491 struct clk *clk;
492
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100493 if (!pdev->dev.of_node)
494 return -ENODEV;
495
496 id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
497 if (!id)
498 return -ENODEV;
499
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100500 axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
501 if (!axi_clkgen)
502 return -ENOMEM;
503
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100504 axi_clkgen->mmcm_ops = id->data;
505
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100506 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
507 axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
508 if (IS_ERR(axi_clkgen->base))
509 return PTR_ERR(axi_clkgen->base);
510
511 parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
512 if (!parent_name)
513 return -EINVAL;
514
515 clk_name = pdev->dev.of_node->name;
516 of_property_read_string(pdev->dev.of_node, "clock-output-names",
517 &clk_name);
518
519 init.name = clk_name;
520 init.ops = &axi_clkgen_ops;
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100521 init.flags = CLK_SET_RATE_GATE;
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100522 init.parent_names = &parent_name;
523 init.num_parents = 1;
524
Lars-Peter Clausen1887c3a2014-02-17 10:31:53 +0100525 axi_clkgen_mmcm_enable(axi_clkgen, false);
526
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100527 axi_clkgen->clk_hw.init = &init;
528 clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
529 if (IS_ERR(clk))
530 return PTR_ERR(clk);
531
532 return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
533 clk);
534}
535
536static int axi_clkgen_remove(struct platform_device *pdev)
537{
538 of_clk_del_provider(pdev->dev.of_node);
539
540 return 0;
541}
542
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100543static struct platform_driver axi_clkgen_driver = {
544 .driver = {
545 .name = "adi-axi-clkgen",
Lars-Peter Clausen0e646c52013-03-11 16:22:29 +0100546 .of_match_table = axi_clkgen_ids,
547 },
548 .probe = axi_clkgen_probe,
549 .remove = axi_clkgen_remove,
550};
551module_platform_driver(axi_clkgen_driver);
552
553MODULE_LICENSE("GPL v2");
554MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
555MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");