blob: 4b2451129d44ef596708ef9ae8120e37334f08fd [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
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}