aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorThomas Abraham <thomas.abraham@linaro.org>2013-03-09 03:02:48 -0500
committerKukjin Kim <kgene.kim@samsung.com>2013-03-25 05:16:37 -0400
commit1c4c5fe0b787ab02bf7c01b091e82e8d09ee1d64 (patch)
tree2d1d7b1d807413bf32f05b3c444470a6c67062a5 /drivers/clk
parent721c42a351b113a9633d2447d9131fe620f2d805 (diff)
clk: samsung: add pll clock registration helper functions
There are several types of pll clocks used in Samsung SoC's and these pll clocks can be represented as Samsung specific pll clock types and registered with the common clock framework. Add support for pll35xx, pll36xx, pll45xx, pll46xx and pll2550x clock types and helper functions to register them. Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Tested-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Reviewed-by: Tomasz Figa <t.figa@samsung.com> Tested-by: Tomasz Figa <t.figa@samsung.com> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org> Acked-by: Mike Turquette <mturquette@linaro.org> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/samsung/Makefile2
-rw-r--r--drivers/clk/samsung/clk-pll.c499
-rw-r--r--drivers/clk/samsung/clk-pll.h41
3 files changed, 541 insertions, 1 deletions
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
index bd920b43f6e7..78e5aaa55b40 100644
--- a/drivers/clk/samsung/Makefile
+++ b/drivers/clk/samsung/Makefile
@@ -2,4 +2,4 @@
2# Samsung Clock specific Makefile 2# Samsung Clock specific Makefile
3# 3#
4 4
5obj-$(CONFIG_COMMON_CLK) += clk.o 5obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
new file mode 100644
index 000000000000..4b2451129d44
--- /dev/null
+++ b/drivers/clk/samsung/clk-pll.c
@@ -0,0 +1,499 @@
1/*
2 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3 * Copyright (c) 2013 Linaro Ltd.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This file contains the utility functions to register the pll clocks.
10*/
11
12#include <linux/errno.h>
13#include "clk.h"
14#include "clk-pll.h"
15
16/*
17 * PLL35xx Clock Type
18 */
19
20#define PLL35XX_MDIV_MASK (0x3FF)
21#define PLL35XX_PDIV_MASK (0x3F)
22#define PLL35XX_SDIV_MASK (0x7)
23#define PLL35XX_MDIV_SHIFT (16)
24#define PLL35XX_PDIV_SHIFT (8)
25#define PLL35XX_SDIV_SHIFT (0)
26
27struct samsung_clk_pll35xx {
28 struct clk_hw hw;
29 const void __iomem *con_reg;
30};
31
32#define to_clk_pll35xx(_hw) container_of(_hw, struct samsung_clk_pll35xx, hw)
33
34static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
35 unsigned long parent_rate)
36{
37 struct samsung_clk_pll35xx *pll = to_clk_pll35xx(hw);
38 u32 mdiv, pdiv, sdiv, pll_con;
39 u64 fvco = parent_rate;
40
41 pll_con = __raw_readl(pll->con_reg);
42 mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
43 pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
44 sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK;
45
46 fvco *= mdiv;
47 do_div(fvco, (pdiv << sdiv));
48
49 return (unsigned long)fvco;
50}
51
52/* todo: implement pl35xx clock round rate operation */
53static long samsung_pll35xx_round_rate(struct clk_hw *hw,
54 unsigned long drate, unsigned long *prate)
55{
56 return -ENOTSUPP;
57}
58
59/* todo: implement pl35xx clock set rate */
60static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate,
61 unsigned long prate)
62{
63 return -ENOTSUPP;
64}
65
66static const struct clk_ops samsung_pll35xx_clk_ops = {
67 .recalc_rate = samsung_pll35xx_recalc_rate,
68 .round_rate = samsung_pll35xx_round_rate,
69 .set_rate = samsung_pll35xx_set_rate,
70};
71
72struct clk * __init samsung_clk_register_pll35xx(const char *name,
73 const char *pname, const void __iomem *con_reg)
74{
75 struct samsung_clk_pll35xx *pll;
76 struct clk *clk;
77 struct clk_init_data init;
78
79 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
80 if (!pll) {
81 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
82 return NULL;
83 }
84
85 init.name = name;
86 init.ops = &samsung_pll35xx_clk_ops;
87 init.flags = CLK_GET_RATE_NOCACHE;
88 init.parent_names = &pname;
89 init.num_parents = 1;
90
91 pll->hw.init = &init;
92 pll->con_reg = con_reg;
93
94 clk = clk_register(NULL, &pll->hw);
95 if (IS_ERR(clk)) {
96 pr_err("%s: failed to register pll clock %s\n", __func__,
97 name);
98 kfree(pll);
99 }
100
101 if (clk_register_clkdev(clk, name, NULL))
102 pr_err("%s: failed to register lookup for %s", __func__, name);
103
104 return clk;
105}
106
107/*
108 * PLL36xx Clock Type
109 */
110
111#define PLL36XX_KDIV_MASK (0xFFFF)
112#define PLL36XX_MDIV_MASK (0x1FF)
113#define PLL36XX_PDIV_MASK (0x3F)
114#define PLL36XX_SDIV_MASK (0x7)
115#define PLL36XX_MDIV_SHIFT (16)
116#define PLL36XX_PDIV_SHIFT (8)
117#define PLL36XX_SDIV_SHIFT (0)
118
119struct samsung_clk_pll36xx {
120 struct clk_hw hw;
121 const void __iomem *con_reg;
122};
123
124#define to_clk_pll36xx(_hw) container_of(_hw, struct samsung_clk_pll36xx, hw)
125
126static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw,
127 unsigned long parent_rate)
128{
129 struct samsung_clk_pll36xx *pll = to_clk_pll36xx(hw);
130 u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1;
131 u64 fvco = parent_rate;
132
133 pll_con0 = __raw_readl(pll->con_reg);
134 pll_con1 = __raw_readl(pll->con_reg + 4);
135 mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK;
136 pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK;
137 sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK;
138 kdiv = pll_con1 & PLL36XX_KDIV_MASK;
139
140 fvco *= (mdiv << 16) + kdiv;
141 do_div(fvco, (pdiv << sdiv));
142 fvco >>= 16;
143
144 return (unsigned long)fvco;
145}
146
147/* todo: implement pl36xx clock round rate operation */
148static long samsung_pll36xx_round_rate(struct clk_hw *hw,
149 unsigned long drate, unsigned long *prate)
150{
151 return -ENOTSUPP;
152}
153
154/* todo: implement pl36xx clock set rate */
155static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate,
156 unsigned long prate)
157{
158 return -ENOTSUPP;
159}
160
161static const struct clk_ops samsung_pll36xx_clk_ops = {
162 .recalc_rate = samsung_pll36xx_recalc_rate,
163 .round_rate = samsung_pll36xx_round_rate,
164 .set_rate = samsung_pll36xx_set_rate,
165};
166
167struct clk * __init samsung_clk_register_pll36xx(const char *name,
168 const char *pname, const void __iomem *con_reg)
169{
170 struct samsung_clk_pll36xx *pll;
171 struct clk *clk;
172 struct clk_init_data init;
173
174 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
175 if (!pll) {
176 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
177 return NULL;
178 }
179
180 init.name = name;
181 init.ops = &samsung_pll36xx_clk_ops;
182 init.flags = CLK_GET_RATE_NOCACHE;
183 init.parent_names = &pname;
184 init.num_parents = 1;
185
186 pll->hw.init = &init;
187 pll->con_reg = con_reg;
188
189 clk = clk_register(NULL, &pll->hw);
190 if (IS_ERR(clk)) {
191 pr_err("%s: failed to register pll clock %s\n", __func__,
192 name);
193 kfree(pll);
194 }
195
196 if (clk_register_clkdev(clk, name, NULL))
197 pr_err("%s: failed to register lookup for %s", __func__, name);
198
199 return clk;
200}
201
202/*
203 * PLL45xx Clock Type
204 */
205
206#define PLL45XX_MDIV_MASK (0x3FF)
207#define PLL45XX_PDIV_MASK (0x3F)
208#define PLL45XX_SDIV_MASK (0x7)
209#define PLL45XX_MDIV_SHIFT (16)
210#define PLL45XX_PDIV_SHIFT (8)
211#define PLL45XX_SDIV_SHIFT (0)
212
213struct samsung_clk_pll45xx {
214 struct clk_hw hw;
215 enum pll45xx_type type;
216 const void __iomem *con_reg;
217};
218
219#define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw)
220
221static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
222 unsigned long parent_rate)
223{
224 struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw);
225 u32 mdiv, pdiv, sdiv, pll_con;
226 u64 fvco = parent_rate;
227
228 pll_con = __raw_readl(pll->con_reg);
229 mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
230 pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
231 sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
232
233 if (pll->type == pll_4508)
234 sdiv = sdiv - 1;
235
236 fvco *= mdiv;
237 do_div(fvco, (pdiv << sdiv));
238
239 return (unsigned long)fvco;
240}
241
242/* todo: implement pl45xx clock round rate operation */
243static long samsung_pll45xx_round_rate(struct clk_hw *hw,
244 unsigned long drate, unsigned long *prate)
245{
246 return -ENOTSUPP;
247}
248
249/* todo: implement pl45xx clock set rate */
250static int samsung_pll45xx_set_rate(struct clk_hw *hw, unsigned long drate,
251 unsigned long prate)
252{
253 return -ENOTSUPP;
254}
255
256static const struct clk_ops samsung_pll45xx_clk_ops = {
257 .recalc_rate = samsung_pll45xx_recalc_rate,
258 .round_rate = samsung_pll45xx_round_rate,
259 .set_rate = samsung_pll45xx_set_rate,
260};
261
262struct clk * __init samsung_clk_register_pll45xx(const char *name,
263 const char *pname, const void __iomem *con_reg,
264 enum pll45xx_type type)
265{
266 struct samsung_clk_pll45xx *pll;
267 struct clk *clk;
268 struct clk_init_data init;
269
270 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
271 if (!pll) {
272 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
273 return NULL;
274 }
275
276 init.name = name;
277 init.ops = &samsung_pll45xx_clk_ops;
278 init.flags = CLK_GET_RATE_NOCACHE;
279 init.parent_names = &pname;
280 init.num_parents = 1;
281
282 pll->hw.init = &init;
283 pll->con_reg = con_reg;
284 pll->type = type;
285
286 clk = clk_register(NULL, &pll->hw);
287 if (IS_ERR(clk)) {
288 pr_err("%s: failed to register pll clock %s\n", __func__,
289 name);
290 kfree(pll);
291 }
292
293 if (clk_register_clkdev(clk, name, NULL))
294 pr_err("%s: failed to register lookup for %s", __func__, name);
295
296 return clk;
297}
298
299/*
300 * PLL46xx Clock Type
301 */
302
303#define PLL46XX_MDIV_MASK (0x1FF)
304#define PLL46XX_PDIV_MASK (0x3F)
305#define PLL46XX_SDIV_MASK (0x7)
306#define PLL46XX_MDIV_SHIFT (16)
307#define PLL46XX_PDIV_SHIFT (8)
308#define PLL46XX_SDIV_SHIFT (0)
309
310#define PLL46XX_KDIV_MASK (0xFFFF)
311#define PLL4650C_KDIV_MASK (0xFFF)
312#define PLL46XX_KDIV_SHIFT (0)
313
314struct samsung_clk_pll46xx {
315 struct clk_hw hw;
316 enum pll46xx_type type;
317 const void __iomem *con_reg;
318};
319
320#define to_clk_pll46xx(_hw) container_of(_hw, struct samsung_clk_pll46xx, hw)
321
322static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
323 unsigned long parent_rate)
324{
325 struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw);
326 u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift;
327 u64 fvco = parent_rate;
328
329 pll_con0 = __raw_readl(pll->con_reg);
330 pll_con1 = __raw_readl(pll->con_reg + 4);
331 mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
332 pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
333 sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
334 kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK :
335 pll_con1 & PLL46XX_KDIV_MASK;
336
337 shift = pll->type == pll_4600 ? 16 : 10;
338 fvco *= (mdiv << shift) + kdiv;
339 do_div(fvco, (pdiv << sdiv));
340 fvco >>= shift;
341
342 return (unsigned long)fvco;
343}
344
345/* todo: implement pl46xx clock round rate operation */
346static long samsung_pll46xx_round_rate(struct clk_hw *hw,
347 unsigned long drate, unsigned long *prate)
348{
349 return -ENOTSUPP;
350}
351
352/* todo: implement pl46xx clock set rate */
353static int samsung_pll46xx_set_rate(struct clk_hw *hw, unsigned long drate,
354 unsigned long prate)
355{
356 return -ENOTSUPP;
357}
358
359static const struct clk_ops samsung_pll46xx_clk_ops = {
360 .recalc_rate = samsung_pll46xx_recalc_rate,
361 .round_rate = samsung_pll46xx_round_rate,
362 .set_rate = samsung_pll46xx_set_rate,
363};
364
365struct clk * __init samsung_clk_register_pll46xx(const char *name,
366 const char *pname, const void __iomem *con_reg,
367 enum pll46xx_type type)
368{
369 struct samsung_clk_pll46xx *pll;
370 struct clk *clk;
371 struct clk_init_data init;
372
373 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
374 if (!pll) {
375 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
376 return NULL;
377 }
378
379 init.name = name;
380 init.ops = &samsung_pll46xx_clk_ops;
381 init.flags = CLK_GET_RATE_NOCACHE;
382 init.parent_names = &pname;
383 init.num_parents = 1;
384
385 pll->hw.init = &init;
386 pll->con_reg = con_reg;
387 pll->type = type;
388
389 clk = clk_register(NULL, &pll->hw);
390 if (IS_ERR(clk)) {
391 pr_err("%s: failed to register pll clock %s\n", __func__,
392 name);
393 kfree(pll);
394 }
395
396 if (clk_register_clkdev(clk, name, NULL))
397 pr_err("%s: failed to register lookup for %s", __func__, name);
398
399 return clk;
400}
401
402/*
403 * PLL2550x Clock Type
404 */
405
406#define PLL2550X_R_MASK (0x1)
407#define PLL2550X_P_MASK (0x3F)
408#define PLL2550X_M_MASK (0x3FF)
409#define PLL2550X_S_MASK (0x7)
410#define PLL2550X_R_SHIFT (20)
411#define PLL2550X_P_SHIFT (14)
412#define PLL2550X_M_SHIFT (4)
413#define PLL2550X_S_SHIFT (0)
414
415struct samsung_clk_pll2550x {
416 struct clk_hw hw;
417 const void __iomem *reg_base;
418 unsigned long offset;
419};
420
421#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
422
423static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
424 unsigned long parent_rate)
425{
426 struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
427 u32 r, p, m, s, pll_stat;
428 u64 fvco = parent_rate;
429
430 pll_stat = __raw_readl(pll->reg_base + pll->offset * 3);
431 r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
432 if (!r)
433 return 0;
434 p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK;
435 m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK;
436 s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK;
437
438 fvco *= m;
439 do_div(fvco, (p << s));
440
441 return (unsigned long)fvco;
442}
443
444/* todo: implement pl2550x clock round rate operation */
445static long samsung_pll2550x_round_rate(struct clk_hw *hw,
446 unsigned long drate, unsigned long *prate)
447{
448 return -ENOTSUPP;
449}
450
451/* todo: implement pl2550x clock set rate */
452static int samsung_pll2550x_set_rate(struct clk_hw *hw, unsigned long drate,
453 unsigned long prate)
454{
455 return -ENOTSUPP;
456}
457
458static const struct clk_ops samsung_pll2550x_clk_ops = {
459 .recalc_rate = samsung_pll2550x_recalc_rate,
460 .round_rate = samsung_pll2550x_round_rate,
461 .set_rate = samsung_pll2550x_set_rate,
462};
463
464struct clk * __init samsung_clk_register_pll2550x(const char *name,
465 const char *pname, const void __iomem *reg_base,
466 const unsigned long offset)
467{
468 struct samsung_clk_pll2550x *pll;
469 struct clk *clk;
470 struct clk_init_data init;
471
472 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
473 if (!pll) {
474 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
475 return NULL;
476 }
477
478 init.name = name;
479 init.ops = &samsung_pll2550x_clk_ops;
480 init.flags = CLK_GET_RATE_NOCACHE;
481 init.parent_names = &pname;
482 init.num_parents = 1;
483
484 pll->hw.init = &init;
485 pll->reg_base = reg_base;
486 pll->offset = offset;
487
488 clk = clk_register(NULL, &pll->hw);
489 if (IS_ERR(clk)) {
490 pr_err("%s: failed to register pll clock %s\n", __func__,
491 name);
492 kfree(pll);
493 }
494
495 if (clk_register_clkdev(clk, name, NULL))
496 pr_err("%s: failed to register lookup for %s", __func__, name);
497
498 return clk;
499}
diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h
new file mode 100644
index 000000000000..f33786e9a78b
--- /dev/null
+++ b/drivers/clk/samsung/clk-pll.h
@@ -0,0 +1,41 @@
1/*
2 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3 * Copyright (c) 2013 Linaro Ltd.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Common Clock Framework support for all PLL's in Samsung platforms
10*/
11
12#ifndef __SAMSUNG_CLK_PLL_H
13#define __SAMSUNG_CLK_PLL_H
14
15enum pll45xx_type {
16 pll_4500,
17 pll_4502,
18 pll_4508
19};
20
21enum pll46xx_type {
22 pll_4600,
23 pll_4650,
24 pll_4650c,
25};
26
27extern struct clk * __init samsung_clk_register_pll35xx(const char *name,
28 const char *pname, const void __iomem *con_reg);
29extern struct clk * __init samsung_clk_register_pll36xx(const char *name,
30 const char *pname, const void __iomem *con_reg);
31extern struct clk * __init samsung_clk_register_pll45xx(const char *name,
32 const char *pname, const void __iomem *con_reg,
33 enum pll45xx_type type);
34extern struct clk * __init samsung_clk_register_pll46xx(const char *name,
35 const char *pname, const void __iomem *con_reg,
36 enum pll46xx_type type);
37extern struct clk * __init samsung_clk_register_pll2550x(const char *name,
38 const char *pname, const void __iomem *reg_base,
39 const unsigned long offset);
40
41#endif /* __SAMSUNG_CLK_PLL_H */