aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2014-02-17 04:31:53 -0500
committerMike Turquette <mturquette@linaro.org>2014-02-26 20:02:29 -0500
commit1887c3a64fab8300a5be3fb5fd8d6474a63b50a0 (patch)
tree8313a1c3590e3aeec2fa828ca723bf9876e483fc
parent62ac983b614149db5a98f333dbb13848cb6b7524 (diff)
clk: axi-clkgen: Add support for v2
This patch adds support for the new v2 version of the axi-clkgen core. Unfortunately the method of accessing the registers is quite different on v2, while the content still stays largely the same. So the patch adds a small abstraction layer which implements the specific read and write functions for v1 and v2 in callback functions. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
-rw-r--r--Documentation/devicetree/bindings/clock/axi-clkgen.txt2
-rw-r--r--drivers/clk/clk-axi-clkgen.c312
2 files changed, 270 insertions, 44 deletions
diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt
index 028b493e97ff..20e1704e7df2 100644
--- a/Documentation/devicetree/bindings/clock/axi-clkgen.txt
+++ b/Documentation/devicetree/bindings/clock/axi-clkgen.txt
@@ -5,7 +5,7 @@ This binding uses the common clock binding[1].
5[1] Documentation/devicetree/bindings/clock/clock-bindings.txt 5[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
6 6
7Required properties: 7Required properties:
8- compatible : shall be "adi,axi-clkgen". 8- compatible : shall be "adi,axi-clkgen-1.00.a" or "adi,axi-clkgen-2.00.a".
9- #clock-cells : from common clock binding; Should always be set to 0. 9- #clock-cells : from common clock binding; Should always be set to 0.
10- reg : Address and length of the axi-clkgen register set. 10- reg : Address and length of the axi-clkgen register set.
11- clocks : Phandle and clock specifier for the parent clock. 11- clocks : Phandle and clock specifier for the parent clock.
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 8137327847c3..1127ee46b802 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -17,23 +17,75 @@
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/err.h> 18#include <linux/err.h>
19 19
20#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 20#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04
21#define AXI_CLKGEN_REG_CLK_OUT1 0x08 21#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08
22#define AXI_CLKGEN_REG_CLK_OUT2 0x0c 22#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c
23#define AXI_CLKGEN_REG_CLK_DIV 0x10 23#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10
24#define AXI_CLKGEN_REG_CLK_FB1 0x14 24#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14
25#define AXI_CLKGEN_REG_CLK_FB2 0x18 25#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18
26#define AXI_CLKGEN_REG_LOCK1 0x1c 26#define AXI_CLKGEN_V1_REG_LOCK1 0x1c
27#define AXI_CLKGEN_REG_LOCK2 0x20 27#define AXI_CLKGEN_V1_REG_LOCK2 0x20
28#define AXI_CLKGEN_REG_LOCK3 0x24 28#define AXI_CLKGEN_V1_REG_LOCK3 0x24
29#define AXI_CLKGEN_REG_FILTER1 0x28 29#define AXI_CLKGEN_V1_REG_FILTER1 0x28
30#define AXI_CLKGEN_REG_FILTER2 0x2c 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};
31 64
32struct axi_clkgen { 65struct axi_clkgen {
33 void __iomem *base; 66 void __iomem *base;
67 const struct axi_clkgen_mmcm_ops *mmcm_ops;
34 struct clk_hw clk_hw; 68 struct clk_hw clk_hw;
35}; 69};
36 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
37static uint32_t axi_clkgen_lookup_filter(unsigned int m) 89static uint32_t axi_clkgen_lookup_filter(unsigned int m)
38{ 90{
39 switch (m) { 91 switch (m) {
@@ -156,6 +208,148 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
156 *val = readl(axi_clkgen->base + reg); 208 *val = readl(axi_clkgen->base + reg);
157} 209}
158 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, &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
159static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) 353static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
160{ 354{
161 return container_of(clk_hw, struct axi_clkgen, clk_hw); 355 return container_of(clk_hw, struct axi_clkgen, clk_hw);
@@ -184,33 +378,29 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
184 filter = axi_clkgen_lookup_filter(m - 1); 378 filter = axi_clkgen_lookup_filter(m - 1);
185 lock = axi_clkgen_lookup_lock(m - 1); 379 lock = axi_clkgen_lookup_lock(m - 1);
186 380
187 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
188
189 axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); 381 axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
190 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, 382 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1,
191 (high << 6) | low); 383 (high << 6) | low, 0xefff);
192 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, 384 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2,
193 (edge << 7) | (nocount << 6)); 385 (edge << 7) | (nocount << 6), 0x03ff);
194 386
195 axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); 387 axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
196 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, 388 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV,
197 (edge << 13) | (nocount << 12) | (high << 6) | low); 389 (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff);
198 390
199 axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); 391 axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
200 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, 392 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1,
201 (high << 6) | low); 393 (high << 6) | low, 0xefff);
202 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, 394 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2,
203 (edge << 7) | (nocount << 6)); 395 (edge << 7) | (nocount << 6), 0x03ff);
204 396
205 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); 397 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff);
206 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, 398 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2,
207 (((lock >> 16) & 0x1f) << 10) | 0x1); 399 (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff);
208 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, 400 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3,
209 (((lock >> 24) & 0x1f) << 10) | 0x3e9); 401 (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff);
210 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); 402 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900);
211 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); 403 axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900);
212
213 axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
214 404
215 return 0; 405 return 0;
216} 406}
@@ -236,11 +426,11 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
236 unsigned int reg; 426 unsigned int reg;
237 unsigned long long tmp; 427 unsigned long long tmp;
238 428
239 axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, &reg); 429 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
240 dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); 430 dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
241 axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg); 431 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
242 d = (reg & 0x3f) + ((reg >> 6) & 0x3f); 432 d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
243 axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg); 433 axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
244 m = (reg & 0x3f) + ((reg >> 6) & 0x3f); 434 m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
245 435
246 if (d == 0 || dout == 0) 436 if (d == 0 || dout == 0)
@@ -255,14 +445,45 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
255 return tmp; 445 return tmp;
256} 446}
257 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
258static const struct clk_ops axi_clkgen_ops = { 464static const struct clk_ops axi_clkgen_ops = {
259 .recalc_rate = axi_clkgen_recalc_rate, 465 .recalc_rate = axi_clkgen_recalc_rate,
260 .round_rate = axi_clkgen_round_rate, 466 .round_rate = axi_clkgen_round_rate,
261 .set_rate = axi_clkgen_set_rate, 467 .set_rate = axi_clkgen_set_rate,
468 .enable = axi_clkgen_enable,
469 .disable = axi_clkgen_disable,
262}; 470};
263 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
264static int axi_clkgen_probe(struct platform_device *pdev) 484static int axi_clkgen_probe(struct platform_device *pdev)
265{ 485{
486 const struct of_device_id *id;
266 struct axi_clkgen *axi_clkgen; 487 struct axi_clkgen *axi_clkgen;
267 struct clk_init_data init; 488 struct clk_init_data init;
268 const char *parent_name; 489 const char *parent_name;
@@ -270,10 +491,19 @@ static int axi_clkgen_probe(struct platform_device *pdev)
270 struct resource *mem; 491 struct resource *mem;
271 struct clk *clk; 492 struct clk *clk;
272 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
273 axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); 501 axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
274 if (!axi_clkgen) 502 if (!axi_clkgen)
275 return -ENOMEM; 503 return -ENOMEM;
276 504
505 axi_clkgen->mmcm_ops = id->data;
506
277 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 507 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
278 axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); 508 axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
279 if (IS_ERR(axi_clkgen->base)) 509 if (IS_ERR(axi_clkgen->base))
@@ -289,10 +519,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
289 519
290 init.name = clk_name; 520 init.name = clk_name;
291 init.ops = &axi_clkgen_ops; 521 init.ops = &axi_clkgen_ops;
292 init.flags = 0; 522 init.flags = CLK_SET_RATE_GATE;
293 init.parent_names = &parent_name; 523 init.parent_names = &parent_name;
294 init.num_parents = 1; 524 init.num_parents = 1;
295 525
526 axi_clkgen_mmcm_enable(axi_clkgen, false);
527
296 axi_clkgen->clk_hw.init = &init; 528 axi_clkgen->clk_hw.init = &init;
297 clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); 529 clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
298 if (IS_ERR(clk)) 530 if (IS_ERR(clk))
@@ -309,12 +541,6 @@ static int axi_clkgen_remove(struct platform_device *pdev)
309 return 0; 541 return 0;
310} 542}
311 543
312static const struct of_device_id axi_clkgen_ids[] = {
313 { .compatible = "adi,axi-clkgen-1.00.a" },
314 { },
315};
316MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
317
318static struct platform_driver axi_clkgen_driver = { 544static struct platform_driver axi_clkgen_driver = {
319 .driver = { 545 .driver = {
320 .name = "adi-axi-clkgen", 546 .name = "adi-axi-clkgen",