aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnders Berg <anders.berg@lsi.com>2014-05-15 09:42:25 -0400
committerMike Turquette <mturquette@linaro.org>2014-05-23 01:06:14 -0400
commitc675a00c2d666c8e90da335eafbbae81201d53f7 (patch)
treeef80dbe179cefd170734c713e5f266ef6beeb014
parentbb178da701382a230e26d90cf94e8a24b280e0d9 (diff)
clk: Add clock driver for AXM55xx SoC
Add clk driver to support clock blocks found on the AXM55xx devices. The driver provides clock implementations for three different types of clock devices on the AXM55xx device: PLL clock, a clock divider and a clock mux. Signed-off-by: Anders Berg <anders.berg@lsi.com> Cc: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt29
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-axm5516.c615
3 files changed, 645 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt b/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt
new file mode 100644
index 000000000000..3ce97cfe999b
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt
@@ -0,0 +1,29 @@
1AXM5516 clock driver bindings
2-----------------------------
3
4Required properties :
5- compatible : shall contain "lsi,axm5516-clks"
6- reg : shall contain base register location and length
7- #clock-cells : shall contain 1
8
9The consumer specifies the desired clock by having the clock ID in its "clocks"
10phandle cell. See <dt-bindings/clock/lsi,axxia-clock.h> for the list of
11supported clock IDs.
12
13Example:
14
15 clks: clock-controller@2010020000 {
16 compatible = "lsi,axm5516-clks";
17 #clock-cells = <1>;
18 reg = <0x20 0x10020000 0 0x20000>;
19 };
20
21 serial0: uart@2010080000 {
22 compatible = "arm,pl011", "arm,primecell";
23 reg = <0x20 0x10080000 0 0x1000>;
24 interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
25 clocks = <&clks AXXIA_CLK_PER>;
26 clock-names = "apb_pclk";
27 };
28 };
29
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 3e6e577c01db..f30357aa5471 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o
12# hardware specific clock types 12# hardware specific clock types
13# please keep this section sorted lexicographically by file/directory path name 13# please keep this section sorted lexicographically by file/directory path name
14obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o 14obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
15obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
15obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o 16obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
16obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o 17obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
17obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o 18obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
diff --git a/drivers/clk/clk-axm5516.c b/drivers/clk/clk-axm5516.c
new file mode 100644
index 000000000000..d2f1e119b450
--- /dev/null
+++ b/drivers/clk/clk-axm5516.c
@@ -0,0 +1,615 @@
1/*
2 * drivers/clk/clk-axm5516.c
3 *
4 * Provides clock implementations for three different types of clock devices on
5 * the Axxia device: PLL clock, a clock divider and a clock mux.
6 *
7 * Copyright (C) 2014 LSI Corporation
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 */
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/slab.h>
16#include <linux/platform_device.h>
17#include <linux/of.h>
18#include <linux/of_address.h>
19#include <linux/clk-provider.h>
20#include <linux/regmap.h>
21#include <dt-bindings/clock/lsi,axm5516-clks.h>
22
23
24/**
25 * struct axxia_clk - Common struct to all Axxia clocks.
26 * @hw: clk_hw for the common clk framework
27 * @regmap: Regmap for the clock control registers
28 */
29struct axxia_clk {
30 struct clk_hw hw;
31 struct regmap *regmap;
32};
33#define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw)
34
35/**
36 * struct axxia_pllclk - Axxia PLL generated clock.
37 * @aclk: Common struct
38 * @reg: Offset into regmap for PLL control register
39 */
40struct axxia_pllclk {
41 struct axxia_clk aclk;
42 u32 reg;
43};
44#define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk)
45
46/**
47 * axxia_pllclk_recalc - Calculate the PLL generated clock rate given the
48 * parent clock rate.
49 */
50static unsigned long
51axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate)
52{
53 struct axxia_clk *aclk = to_axxia_clk(hw);
54 struct axxia_pllclk *pll = to_axxia_pllclk(aclk);
55 unsigned long rate, fbdiv, refdiv, postdiv;
56 u32 control;
57
58 regmap_read(aclk->regmap, pll->reg, &control);
59 postdiv = ((control >> 0) & 0xf) + 1;
60 fbdiv = ((control >> 4) & 0xfff) + 3;
61 refdiv = ((control >> 16) & 0x1f) + 1;
62 rate = (parent_rate / (refdiv * postdiv)) * fbdiv;
63
64 return rate;
65}
66
67static const struct clk_ops axxia_pllclk_ops = {
68 .recalc_rate = axxia_pllclk_recalc,
69};
70
71/**
72 * struct axxia_divclk - Axxia clock divider
73 * @aclk: Common struct
74 * @reg: Offset into regmap for PLL control register
75 * @shift: Bit position for divider value
76 * @width: Number of bits in divider value
77 */
78struct axxia_divclk {
79 struct axxia_clk aclk;
80 u32 reg;
81 u32 shift;
82 u32 width;
83};
84#define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk)
85
86/**
87 * axxia_divclk_recalc_rate - Calculate clock divider output rage
88 */
89static unsigned long
90axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
91{
92 struct axxia_clk *aclk = to_axxia_clk(hw);
93 struct axxia_divclk *divclk = to_axxia_divclk(aclk);
94 u32 ctrl, div;
95
96 regmap_read(aclk->regmap, divclk->reg, &ctrl);
97 div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1));
98
99 return parent_rate / div;
100}
101
102static const struct clk_ops axxia_divclk_ops = {
103 .recalc_rate = axxia_divclk_recalc_rate,
104};
105
106/**
107 * struct axxia_clkmux - Axxia clock mux
108 * @aclk: Common struct
109 * @reg: Offset into regmap for PLL control register
110 * @shift: Bit position for selection value
111 * @width: Number of bits in selection value
112 */
113struct axxia_clkmux {
114 struct axxia_clk aclk;
115 u32 reg;
116 u32 shift;
117 u32 width;
118};
119#define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk)
120
121/**
122 * axxia_clkmux_get_parent - Return the index of selected parent clock
123 */
124static u8 axxia_clkmux_get_parent(struct clk_hw *hw)
125{
126 struct axxia_clk *aclk = to_axxia_clk(hw);
127 struct axxia_clkmux *mux = to_axxia_clkmux(aclk);
128 u32 ctrl, parent;
129
130 regmap_read(aclk->regmap, mux->reg, &ctrl);
131 parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1);
132
133 return (u8) parent;
134}
135
136static const struct clk_ops axxia_clkmux_ops = {
137 .get_parent = axxia_clkmux_get_parent,
138};
139
140
141/*
142 * PLLs
143 */
144
145static struct axxia_pllclk clk_fab_pll = {
146 .aclk.hw.init = &(struct clk_init_data){
147 .name = "clk_fab_pll",
148 .parent_names = (const char *[]){
149 "clk_ref0"
150 },
151 .num_parents = 1,
152 .ops = &axxia_pllclk_ops,
153 },
154 .reg = 0x01800,
155};
156
157static struct axxia_pllclk clk_cpu_pll = {
158 .aclk.hw.init = &(struct clk_init_data){
159 .name = "clk_cpu_pll",
160 .parent_names = (const char *[]){
161 "clk_ref0"
162 },
163 .num_parents = 1,
164 .ops = &axxia_pllclk_ops,
165 },
166 .reg = 0x02000,
167};
168
169static struct axxia_pllclk clk_sys_pll = {
170 .aclk.hw.init = &(struct clk_init_data){
171 .name = "clk_sys_pll",
172 .parent_names = (const char *[]){
173 "clk_ref0"
174 },
175 .num_parents = 1,
176 .ops = &axxia_pllclk_ops,
177 },
178 .reg = 0x02800,
179};
180
181static struct axxia_pllclk clk_sm0_pll = {
182 .aclk.hw.init = &(struct clk_init_data){
183 .name = "clk_sm0_pll",
184 .parent_names = (const char *[]){
185 "clk_ref2"
186 },
187 .num_parents = 1,
188 .ops = &axxia_pllclk_ops,
189 },
190 .reg = 0x03000,
191};
192
193static struct axxia_pllclk clk_sm1_pll = {
194 .aclk.hw.init = &(struct clk_init_data){
195 .name = "clk_sm1_pll",
196 .parent_names = (const char *[]){
197 "clk_ref1"
198 },
199 .num_parents = 1,
200 .ops = &axxia_pllclk_ops,
201 },
202 .reg = 0x03800,
203};
204
205/*
206 * Clock dividers
207 */
208
209static struct axxia_divclk clk_cpu0_div = {
210 .aclk.hw.init = &(struct clk_init_data){
211 .name = "clk_cpu0_div",
212 .parent_names = (const char *[]){
213 "clk_cpu_pll"
214 },
215 .num_parents = 1,
216 .ops = &axxia_divclk_ops,
217 },
218 .reg = 0x10008,
219 .shift = 0,
220 .width = 4,
221};
222
223static struct axxia_divclk clk_cpu1_div = {
224 .aclk.hw.init = &(struct clk_init_data){
225 .name = "clk_cpu1_div",
226 .parent_names = (const char *[]){
227 "clk_cpu_pll"
228 },
229 .num_parents = 1,
230 .ops = &axxia_divclk_ops,
231 },
232 .reg = 0x10008,
233 .shift = 4,
234 .width = 4,
235};
236
237static struct axxia_divclk clk_cpu2_div = {
238 .aclk.hw.init = &(struct clk_init_data){
239 .name = "clk_cpu2_div",
240 .parent_names = (const char *[]){
241 "clk_cpu_pll"
242 },
243 .num_parents = 1,
244 .ops = &axxia_divclk_ops,
245 },
246 .reg = 0x10008,
247 .shift = 8,
248 .width = 4,
249};
250
251static struct axxia_divclk clk_cpu3_div = {
252 .aclk.hw.init = &(struct clk_init_data){
253 .name = "clk_cpu3_div",
254 .parent_names = (const char *[]){
255 "clk_cpu_pll"
256 },
257 .num_parents = 1,
258 .ops = &axxia_divclk_ops,
259 },
260 .reg = 0x10008,
261 .shift = 12,
262 .width = 4,
263};
264
265static struct axxia_divclk clk_nrcp_div = {
266 .aclk.hw.init = &(struct clk_init_data){
267 .name = "clk_nrcp_div",
268 .parent_names = (const char *[]){
269 "clk_sys_pll"
270 },
271 .num_parents = 1,
272 .ops = &axxia_divclk_ops,
273 },
274 .reg = 0x1000c,
275 .shift = 0,
276 .width = 4,
277};
278
279static struct axxia_divclk clk_sys_div = {
280 .aclk.hw.init = &(struct clk_init_data){
281 .name = "clk_sys_div",
282 .parent_names = (const char *[]){
283 "clk_sys_pll"
284 },
285 .num_parents = 1,
286 .ops = &axxia_divclk_ops,
287 },
288 .reg = 0x1000c,
289 .shift = 4,
290 .width = 4,
291};
292
293static struct axxia_divclk clk_fab_div = {
294 .aclk.hw.init = &(struct clk_init_data){
295 .name = "clk_fab_div",
296 .parent_names = (const char *[]){
297 "clk_fab_pll"
298 },
299 .num_parents = 1,
300 .ops = &axxia_divclk_ops,
301 },
302 .reg = 0x1000c,
303 .shift = 8,
304 .width = 4,
305};
306
307static struct axxia_divclk clk_per_div = {
308 .aclk.hw.init = &(struct clk_init_data){
309 .name = "clk_per_div",
310 .parent_names = (const char *[]){
311 "clk_sm1_pll"
312 },
313 .num_parents = 1,
314 .flags = CLK_IS_BASIC,
315 .ops = &axxia_divclk_ops,
316 },
317 .reg = 0x1000c,
318 .shift = 12,
319 .width = 4,
320};
321
322static struct axxia_divclk clk_mmc_div = {
323 .aclk.hw.init = &(struct clk_init_data){
324 .name = "clk_mmc_div",
325 .parent_names = (const char *[]){
326 "clk_sm1_pll"
327 },
328 .num_parents = 1,
329 .flags = CLK_IS_BASIC,
330 .ops = &axxia_divclk_ops,
331 },
332 .reg = 0x1000c,
333 .shift = 16,
334 .width = 4,
335};
336
337/*
338 * Clock MUXes
339 */
340
341static struct axxia_clkmux clk_cpu0_mux = {
342 .aclk.hw.init = &(struct clk_init_data){
343 .name = "clk_cpu0",
344 .parent_names = (const char *[]){
345 "clk_ref0",
346 "clk_cpu_pll",
347 "clk_cpu0_div",
348 "clk_cpu0_div"
349 },
350 .num_parents = 4,
351 .ops = &axxia_clkmux_ops,
352 },
353 .reg = 0x10000,
354 .shift = 0,
355 .width = 2,
356};
357
358static struct axxia_clkmux clk_cpu1_mux = {
359 .aclk.hw.init = &(struct clk_init_data){
360 .name = "clk_cpu1",
361 .parent_names = (const char *[]){
362 "clk_ref0",
363 "clk_cpu_pll",
364 "clk_cpu1_div",
365 "clk_cpu1_div"
366 },
367 .num_parents = 4,
368 .ops = &axxia_clkmux_ops,
369 },
370 .reg = 0x10000,
371 .shift = 2,
372 .width = 2,
373};
374
375static struct axxia_clkmux clk_cpu2_mux = {
376 .aclk.hw.init = &(struct clk_init_data){
377 .name = "clk_cpu2",
378 .parent_names = (const char *[]){
379 "clk_ref0",
380 "clk_cpu_pll",
381 "clk_cpu2_div",
382 "clk_cpu2_div"
383 },
384 .num_parents = 4,
385 .ops = &axxia_clkmux_ops,
386 },
387 .reg = 0x10000,
388 .shift = 4,
389 .width = 2,
390};
391
392static struct axxia_clkmux clk_cpu3_mux = {
393 .aclk.hw.init = &(struct clk_init_data){
394 .name = "clk_cpu3",
395 .parent_names = (const char *[]){
396 "clk_ref0",
397 "clk_cpu_pll",
398 "clk_cpu3_div",
399 "clk_cpu3_div"
400 },
401 .num_parents = 4,
402 .ops = &axxia_clkmux_ops,
403 },
404 .reg = 0x10000,
405 .shift = 6,
406 .width = 2,
407};
408
409static struct axxia_clkmux clk_nrcp_mux = {
410 .aclk.hw.init = &(struct clk_init_data){
411 .name = "clk_nrcp",
412 .parent_names = (const char *[]){
413 "clk_ref0",
414 "clk_sys_pll",
415 "clk_nrcp_div",
416 "clk_nrcp_div"
417 },
418 .num_parents = 4,
419 .ops = &axxia_clkmux_ops,
420 },
421 .reg = 0x10004,
422 .shift = 0,
423 .width = 2,
424};
425
426static struct axxia_clkmux clk_sys_mux = {
427 .aclk.hw.init = &(struct clk_init_data){
428 .name = "clk_sys",
429 .parent_names = (const char *[]){
430 "clk_ref0",
431 "clk_sys_pll",
432 "clk_sys_div",
433 "clk_sys_div"
434 },
435 .num_parents = 4,
436 .ops = &axxia_clkmux_ops,
437 },
438 .reg = 0x10004,
439 .shift = 2,
440 .width = 2,
441};
442
443static struct axxia_clkmux clk_fab_mux = {
444 .aclk.hw.init = &(struct clk_init_data){
445 .name = "clk_fab",
446 .parent_names = (const char *[]){
447 "clk_ref0",
448 "clk_fab_pll",
449 "clk_fab_div",
450 "clk_fab_div"
451 },
452 .num_parents = 4,
453 .ops = &axxia_clkmux_ops,
454 },
455 .reg = 0x10004,
456 .shift = 4,
457 .width = 2,
458};
459
460static struct axxia_clkmux clk_per_mux = {
461 .aclk.hw.init = &(struct clk_init_data){
462 .name = "clk_per",
463 .parent_names = (const char *[]){
464 "clk_ref1",
465 "clk_per_div"
466 },
467 .num_parents = 2,
468 .ops = &axxia_clkmux_ops,
469 },
470 .reg = 0x10004,
471 .shift = 6,
472 .width = 1,
473};
474
475static struct axxia_clkmux clk_mmc_mux = {
476 .aclk.hw.init = &(struct clk_init_data){
477 .name = "clk_mmc",
478 .parent_names = (const char *[]){
479 "clk_ref1",
480 "clk_mmc_div"
481 },
482 .num_parents = 2,
483 .ops = &axxia_clkmux_ops,
484 },
485 .reg = 0x10004,
486 .shift = 9,
487 .width = 1,
488};
489
490/* Table of all supported clocks indexed by the clock identifiers from the
491 * device tree binding
492 */
493static struct axxia_clk *axmclk_clocks[] = {
494 [AXXIA_CLK_FAB_PLL] = &clk_fab_pll.aclk,
495 [AXXIA_CLK_CPU_PLL] = &clk_cpu_pll.aclk,
496 [AXXIA_CLK_SYS_PLL] = &clk_sys_pll.aclk,
497 [AXXIA_CLK_SM0_PLL] = &clk_sm0_pll.aclk,
498 [AXXIA_CLK_SM1_PLL] = &clk_sm1_pll.aclk,
499 [AXXIA_CLK_FAB_DIV] = &clk_fab_div.aclk,
500 [AXXIA_CLK_SYS_DIV] = &clk_sys_div.aclk,
501 [AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk,
502 [AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk,
503 [AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk,
504 [AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk,
505 [AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk,
506 [AXXIA_CLK_PER_DIV] = &clk_per_div.aclk,
507 [AXXIA_CLK_MMC_DIV] = &clk_mmc_div.aclk,
508 [AXXIA_CLK_FAB] = &clk_fab_mux.aclk,
509 [AXXIA_CLK_SYS] = &clk_sys_mux.aclk,
510 [AXXIA_CLK_NRCP] = &clk_nrcp_mux.aclk,
511 [AXXIA_CLK_CPU0] = &clk_cpu0_mux.aclk,
512 [AXXIA_CLK_CPU1] = &clk_cpu1_mux.aclk,
513 [AXXIA_CLK_CPU2] = &clk_cpu2_mux.aclk,
514 [AXXIA_CLK_CPU3] = &clk_cpu3_mux.aclk,
515 [AXXIA_CLK_PER] = &clk_per_mux.aclk,
516 [AXXIA_CLK_MMC] = &clk_mmc_mux.aclk,
517};
518
519static const struct regmap_config axmclk_regmap_config = {
520 .reg_bits = 32,
521 .reg_stride = 4,
522 .val_bits = 32,
523 .max_register = 0x1fffc,
524 .fast_io = true,
525};
526
527static const struct of_device_id axmclk_match_table[] = {
528 { .compatible = "lsi,axm5516-clks" },
529 { }
530};
531MODULE_DEVICE_TABLE(of, axmclk_match_table);
532
533struct axmclk_priv {
534 struct clk_onecell_data onecell;
535 struct clk *clks[];
536};
537
538static int axmclk_probe(struct platform_device *pdev)
539{
540 void __iomem *base;
541 struct resource *res;
542 int i, ret;
543 struct device *dev = &pdev->dev;
544 struct clk *clk;
545 struct regmap *regmap;
546 size_t num_clks;
547 struct axmclk_priv *priv;
548
549 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
550 base = devm_ioremap_resource(dev, res);
551 if (IS_ERR(base))
552 return PTR_ERR(base);
553
554 regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config);
555 if (IS_ERR(regmap))
556 return PTR_ERR(regmap);
557
558 num_clks = ARRAY_SIZE(axmclk_clocks);
559 pr_info("axmclk: supporting %u clocks\n", num_clks);
560 priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks,
561 GFP_KERNEL);
562 if (!priv)
563 return -ENOMEM;
564
565 priv->onecell.clks = priv->clks;
566 priv->onecell.clk_num = num_clks;
567
568 /* Update each entry with the allocated regmap and register the clock
569 * with the common clock framework
570 */
571 for (i = 0; i < num_clks; i++) {
572 axmclk_clocks[i]->regmap = regmap;
573 clk = devm_clk_register(dev, &axmclk_clocks[i]->hw);
574 if (IS_ERR(clk))
575 return PTR_ERR(clk);
576 priv->clks[i] = clk;
577 }
578
579 ret = of_clk_add_provider(dev->of_node,
580 of_clk_src_onecell_get, &priv->onecell);
581
582 return ret;
583}
584
585static int axmclk_remove(struct platform_device *pdev)
586{
587 of_clk_del_provider(pdev->dev.of_node);
588 return 0;
589}
590
591static struct platform_driver axmclk_driver = {
592 .probe = axmclk_probe,
593 .remove = axmclk_remove,
594 .driver = {
595 .name = "clk-axm5516",
596 .owner = THIS_MODULE,
597 .of_match_table = axmclk_match_table,
598 },
599};
600
601static int __init axmclk_init(void)
602{
603 return platform_driver_register(&axmclk_driver);
604}
605core_initcall(axmclk_init);
606
607static void __exit axmclk_exit(void)
608{
609 platform_driver_unregister(&axmclk_driver);
610}
611module_exit(axmclk_exit);
612
613MODULE_DESCRIPTION("AXM5516 clock driver");
614MODULE_LICENSE("GPL v2");
615MODULE_ALIAS("platform:clk-axm5516");