blob: 698e5621b6e72c2d18f7f0ec6d53ee805c50bc04 [file] [log] [blame]
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +09001/*
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
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +090052static const struct clk_ops samsung_pll35xx_clk_ops = {
53 .recalc_rate = samsung_pll35xx_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +090054};
55
56struct clk * __init samsung_clk_register_pll35xx(const char *name,
57 const char *pname, const void __iomem *con_reg)
58{
59 struct samsung_clk_pll35xx *pll;
60 struct clk *clk;
61 struct clk_init_data init;
62
63 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
64 if (!pll) {
65 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
66 return NULL;
67 }
68
69 init.name = name;
70 init.ops = &samsung_pll35xx_clk_ops;
71 init.flags = CLK_GET_RATE_NOCACHE;
72 init.parent_names = &pname;
73 init.num_parents = 1;
74
75 pll->hw.init = &init;
76 pll->con_reg = con_reg;
77
78 clk = clk_register(NULL, &pll->hw);
79 if (IS_ERR(clk)) {
80 pr_err("%s: failed to register pll clock %s\n", __func__,
81 name);
82 kfree(pll);
83 }
84
85 if (clk_register_clkdev(clk, name, NULL))
86 pr_err("%s: failed to register lookup for %s", __func__, name);
87
88 return clk;
89}
90
91/*
92 * PLL36xx Clock Type
93 */
94
95#define PLL36XX_KDIV_MASK (0xFFFF)
96#define PLL36XX_MDIV_MASK (0x1FF)
97#define PLL36XX_PDIV_MASK (0x3F)
98#define PLL36XX_SDIV_MASK (0x7)
99#define PLL36XX_MDIV_SHIFT (16)
100#define PLL36XX_PDIV_SHIFT (8)
101#define PLL36XX_SDIV_SHIFT (0)
102
103struct samsung_clk_pll36xx {
104 struct clk_hw hw;
105 const void __iomem *con_reg;
106};
107
108#define to_clk_pll36xx(_hw) container_of(_hw, struct samsung_clk_pll36xx, hw)
109
110static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw,
111 unsigned long parent_rate)
112{
113 struct samsung_clk_pll36xx *pll = to_clk_pll36xx(hw);
Doug Anderson071ff9a2013-06-11 08:24:05 -0700114 u32 mdiv, pdiv, sdiv, pll_con0, pll_con1;
115 s16 kdiv;
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900116 u64 fvco = parent_rate;
117
118 pll_con0 = __raw_readl(pll->con_reg);
119 pll_con1 = __raw_readl(pll->con_reg + 4);
120 mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK;
121 pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK;
122 sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK;
Doug Anderson071ff9a2013-06-11 08:24:05 -0700123 kdiv = (s16)(pll_con1 & PLL36XX_KDIV_MASK);
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900124
125 fvco *= (mdiv << 16) + kdiv;
126 do_div(fvco, (pdiv << sdiv));
127 fvco >>= 16;
128
129 return (unsigned long)fvco;
130}
131
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900132static const struct clk_ops samsung_pll36xx_clk_ops = {
133 .recalc_rate = samsung_pll36xx_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900134};
135
136struct clk * __init samsung_clk_register_pll36xx(const char *name,
137 const char *pname, const void __iomem *con_reg)
138{
139 struct samsung_clk_pll36xx *pll;
140 struct clk *clk;
141 struct clk_init_data init;
142
143 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
144 if (!pll) {
145 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
146 return NULL;
147 }
148
149 init.name = name;
150 init.ops = &samsung_pll36xx_clk_ops;
151 init.flags = CLK_GET_RATE_NOCACHE;
152 init.parent_names = &pname;
153 init.num_parents = 1;
154
155 pll->hw.init = &init;
156 pll->con_reg = con_reg;
157
158 clk = clk_register(NULL, &pll->hw);
159 if (IS_ERR(clk)) {
160 pr_err("%s: failed to register pll clock %s\n", __func__,
161 name);
162 kfree(pll);
163 }
164
165 if (clk_register_clkdev(clk, name, NULL))
166 pr_err("%s: failed to register lookup for %s", __func__, name);
167
168 return clk;
169}
170
171/*
172 * PLL45xx Clock Type
173 */
174
175#define PLL45XX_MDIV_MASK (0x3FF)
176#define PLL45XX_PDIV_MASK (0x3F)
177#define PLL45XX_SDIV_MASK (0x7)
178#define PLL45XX_MDIV_SHIFT (16)
179#define PLL45XX_PDIV_SHIFT (8)
180#define PLL45XX_SDIV_SHIFT (0)
181
182struct samsung_clk_pll45xx {
183 struct clk_hw hw;
184 enum pll45xx_type type;
185 const void __iomem *con_reg;
186};
187
188#define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw)
189
190static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
191 unsigned long parent_rate)
192{
193 struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw);
194 u32 mdiv, pdiv, sdiv, pll_con;
195 u64 fvco = parent_rate;
196
197 pll_con = __raw_readl(pll->con_reg);
198 mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
199 pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
200 sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
201
202 if (pll->type == pll_4508)
203 sdiv = sdiv - 1;
204
205 fvco *= mdiv;
206 do_div(fvco, (pdiv << sdiv));
207
208 return (unsigned long)fvco;
209}
210
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900211static const struct clk_ops samsung_pll45xx_clk_ops = {
212 .recalc_rate = samsung_pll45xx_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900213};
214
215struct clk * __init samsung_clk_register_pll45xx(const char *name,
216 const char *pname, const void __iomem *con_reg,
217 enum pll45xx_type type)
218{
219 struct samsung_clk_pll45xx *pll;
220 struct clk *clk;
221 struct clk_init_data init;
222
223 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
224 if (!pll) {
225 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
226 return NULL;
227 }
228
229 init.name = name;
230 init.ops = &samsung_pll45xx_clk_ops;
231 init.flags = CLK_GET_RATE_NOCACHE;
232 init.parent_names = &pname;
233 init.num_parents = 1;
234
235 pll->hw.init = &init;
236 pll->con_reg = con_reg;
237 pll->type = type;
238
239 clk = clk_register(NULL, &pll->hw);
240 if (IS_ERR(clk)) {
241 pr_err("%s: failed to register pll clock %s\n", __func__,
242 name);
243 kfree(pll);
244 }
245
246 if (clk_register_clkdev(clk, name, NULL))
247 pr_err("%s: failed to register lookup for %s", __func__, name);
248
249 return clk;
250}
251
252/*
253 * PLL46xx Clock Type
254 */
255
256#define PLL46XX_MDIV_MASK (0x1FF)
257#define PLL46XX_PDIV_MASK (0x3F)
258#define PLL46XX_SDIV_MASK (0x7)
259#define PLL46XX_MDIV_SHIFT (16)
260#define PLL46XX_PDIV_SHIFT (8)
261#define PLL46XX_SDIV_SHIFT (0)
262
263#define PLL46XX_KDIV_MASK (0xFFFF)
264#define PLL4650C_KDIV_MASK (0xFFF)
265#define PLL46XX_KDIV_SHIFT (0)
266
267struct samsung_clk_pll46xx {
268 struct clk_hw hw;
269 enum pll46xx_type type;
270 const void __iomem *con_reg;
271};
272
273#define to_clk_pll46xx(_hw) container_of(_hw, struct samsung_clk_pll46xx, hw)
274
275static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
276 unsigned long parent_rate)
277{
278 struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw);
279 u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift;
280 u64 fvco = parent_rate;
281
282 pll_con0 = __raw_readl(pll->con_reg);
283 pll_con1 = __raw_readl(pll->con_reg + 4);
284 mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
285 pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
286 sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
287 kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK :
288 pll_con1 & PLL46XX_KDIV_MASK;
289
290 shift = pll->type == pll_4600 ? 16 : 10;
291 fvco *= (mdiv << shift) + kdiv;
292 do_div(fvco, (pdiv << sdiv));
293 fvco >>= shift;
294
295 return (unsigned long)fvco;
296}
297
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900298static const struct clk_ops samsung_pll46xx_clk_ops = {
299 .recalc_rate = samsung_pll46xx_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900300};
301
302struct clk * __init samsung_clk_register_pll46xx(const char *name,
303 const char *pname, const void __iomem *con_reg,
304 enum pll46xx_type type)
305{
306 struct samsung_clk_pll46xx *pll;
307 struct clk *clk;
308 struct clk_init_data init;
309
310 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
311 if (!pll) {
312 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
313 return NULL;
314 }
315
316 init.name = name;
317 init.ops = &samsung_pll46xx_clk_ops;
318 init.flags = CLK_GET_RATE_NOCACHE;
319 init.parent_names = &pname;
320 init.num_parents = 1;
321
322 pll->hw.init = &init;
323 pll->con_reg = con_reg;
324 pll->type = type;
325
326 clk = clk_register(NULL, &pll->hw);
327 if (IS_ERR(clk)) {
328 pr_err("%s: failed to register pll clock %s\n", __func__,
329 name);
330 kfree(pll);
331 }
332
333 if (clk_register_clkdev(clk, name, NULL))
334 pr_err("%s: failed to register lookup for %s", __func__, name);
335
336 return clk;
337}
338
339/*
Tomasz Figaeb527122013-07-23 01:49:19 +0200340 * PLL6552 Clock Type
341 */
342
343#define PLL6552_LOCK_REG 0x00
344#define PLL6552_CON_REG 0x0c
345
346#define PLL6552_MDIV_MASK 0x3ff
347#define PLL6552_PDIV_MASK 0x3f
348#define PLL6552_SDIV_MASK 0x7
349#define PLL6552_MDIV_SHIFT 16
350#define PLL6552_PDIV_SHIFT 8
351#define PLL6552_SDIV_SHIFT 0
352
353struct samsung_clk_pll6552 {
354 struct clk_hw hw;
355 void __iomem *reg_base;
356};
357
358#define to_clk_pll6552(_hw) container_of(_hw, struct samsung_clk_pll6552, hw)
359
360static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw,
361 unsigned long parent_rate)
362{
363 struct samsung_clk_pll6552 *pll = to_clk_pll6552(hw);
364 u32 mdiv, pdiv, sdiv, pll_con;
365 u64 fvco = parent_rate;
366
367 pll_con = __raw_readl(pll->reg_base + PLL6552_CON_REG);
368 mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK;
369 pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK;
370 sdiv = (pll_con >> PLL6552_SDIV_SHIFT) & PLL6552_SDIV_MASK;
371
372 fvco *= mdiv;
373 do_div(fvco, (pdiv << sdiv));
374
375 return (unsigned long)fvco;
376}
377
378static const struct clk_ops samsung_pll6552_clk_ops = {
379 .recalc_rate = samsung_pll6552_recalc_rate,
380};
381
382struct clk * __init samsung_clk_register_pll6552(const char *name,
383 const char *pname, void __iomem *base)
384{
385 struct samsung_clk_pll6552 *pll;
386 struct clk *clk;
387 struct clk_init_data init;
388
389 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
390 if (!pll) {
391 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
392 return NULL;
393 }
394
395 init.name = name;
396 init.ops = &samsung_pll6552_clk_ops;
397 init.parent_names = &pname;
398 init.num_parents = 1;
399
400 pll->hw.init = &init;
401 pll->reg_base = base;
402
403 clk = clk_register(NULL, &pll->hw);
404 if (IS_ERR(clk)) {
405 pr_err("%s: failed to register pll clock %s\n", __func__,
406 name);
407 kfree(pll);
408 }
409
410 if (clk_register_clkdev(clk, name, NULL))
411 pr_err("%s: failed to register lookup for %s", __func__, name);
412
413 return clk;
414}
415
416/*
417 * PLL6553 Clock Type
418 */
419
420#define PLL6553_LOCK_REG 0x00
421#define PLL6553_CON0_REG 0x0c
422#define PLL6553_CON1_REG 0x10
423
424#define PLL6553_MDIV_MASK 0xff
425#define PLL6553_PDIV_MASK 0x3f
426#define PLL6553_SDIV_MASK 0x7
427#define PLL6553_KDIV_MASK 0xffff
428#define PLL6553_MDIV_SHIFT 16
429#define PLL6553_PDIV_SHIFT 8
430#define PLL6553_SDIV_SHIFT 0
431#define PLL6553_KDIV_SHIFT 0
432
433struct samsung_clk_pll6553 {
434 struct clk_hw hw;
435 void __iomem *reg_base;
436};
437
438#define to_clk_pll6553(_hw) container_of(_hw, struct samsung_clk_pll6553, hw)
439
440static unsigned long samsung_pll6553_recalc_rate(struct clk_hw *hw,
441 unsigned long parent_rate)
442{
443 struct samsung_clk_pll6553 *pll = to_clk_pll6553(hw);
444 u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1;
445 u64 fvco = parent_rate;
446
447 pll_con0 = __raw_readl(pll->reg_base + PLL6553_CON0_REG);
448 pll_con1 = __raw_readl(pll->reg_base + PLL6553_CON1_REG);
449 mdiv = (pll_con0 >> PLL6553_MDIV_SHIFT) & PLL6553_MDIV_MASK;
450 pdiv = (pll_con0 >> PLL6553_PDIV_SHIFT) & PLL6553_PDIV_MASK;
451 sdiv = (pll_con0 >> PLL6553_SDIV_SHIFT) & PLL6553_SDIV_MASK;
452 kdiv = (pll_con1 >> PLL6553_KDIV_SHIFT) & PLL6553_KDIV_MASK;
453
454 fvco *= (mdiv << 16) + kdiv;
455 do_div(fvco, (pdiv << sdiv));
456 fvco >>= 16;
457
458 return (unsigned long)fvco;
459}
460
461static const struct clk_ops samsung_pll6553_clk_ops = {
462 .recalc_rate = samsung_pll6553_recalc_rate,
463};
464
465struct clk * __init samsung_clk_register_pll6553(const char *name,
466 const char *pname, void __iomem *base)
467{
468 struct samsung_clk_pll6553 *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_pll6553_clk_ops;
480 init.parent_names = &pname;
481 init.num_parents = 1;
482
483 pll->hw.init = &init;
484 pll->reg_base = base;
485
486 clk = clk_register(NULL, &pll->hw);
487 if (IS_ERR(clk)) {
488 pr_err("%s: failed to register pll clock %s\n", __func__,
489 name);
490 kfree(pll);
491 }
492
493 if (clk_register_clkdev(clk, name, NULL))
494 pr_err("%s: failed to register lookup for %s", __func__, name);
495
496 return clk;
497}
498
499/*
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900500 * PLL2550x Clock Type
501 */
502
503#define PLL2550X_R_MASK (0x1)
504#define PLL2550X_P_MASK (0x3F)
505#define PLL2550X_M_MASK (0x3FF)
506#define PLL2550X_S_MASK (0x7)
507#define PLL2550X_R_SHIFT (20)
508#define PLL2550X_P_SHIFT (14)
509#define PLL2550X_M_SHIFT (4)
510#define PLL2550X_S_SHIFT (0)
511
512struct samsung_clk_pll2550x {
513 struct clk_hw hw;
514 const void __iomem *reg_base;
515 unsigned long offset;
516};
517
518#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
519
520static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
521 unsigned long parent_rate)
522{
523 struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
524 u32 r, p, m, s, pll_stat;
525 u64 fvco = parent_rate;
526
527 pll_stat = __raw_readl(pll->reg_base + pll->offset * 3);
528 r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
529 if (!r)
530 return 0;
531 p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK;
532 m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK;
533 s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK;
534
535 fvco *= m;
536 do_div(fvco, (p << s));
537
538 return (unsigned long)fvco;
539}
540
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900541static const struct clk_ops samsung_pll2550x_clk_ops = {
542 .recalc_rate = samsung_pll2550x_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900543};
544
545struct clk * __init samsung_clk_register_pll2550x(const char *name,
546 const char *pname, const void __iomem *reg_base,
547 const unsigned long offset)
548{
549 struct samsung_clk_pll2550x *pll;
550 struct clk *clk;
551 struct clk_init_data init;
552
553 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
554 if (!pll) {
555 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
556 return NULL;
557 }
558
559 init.name = name;
560 init.ops = &samsung_pll2550x_clk_ops;
561 init.flags = CLK_GET_RATE_NOCACHE;
562 init.parent_names = &pname;
563 init.num_parents = 1;
564
565 pll->hw.init = &init;
566 pll->reg_base = reg_base;
567 pll->offset = offset;
568
569 clk = clk_register(NULL, &pll->hw);
570 if (IS_ERR(clk)) {
571 pr_err("%s: failed to register pll clock %s\n", __func__,
572 name);
573 kfree(pll);
574 }
575
576 if (clk_register_clkdev(clk, name, NULL))
577 pr_err("%s: failed to register lookup for %s", __func__, name);
578
579 return clk;
580}