blob: 03486e339b649f544fac41407060feed3d350dee [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
Yadwinder Singh Brar079dbea2013-06-11 15:01:06 +053016struct samsung_clk_pll {
17 struct clk_hw hw;
18 void __iomem *lock_reg;
19 void __iomem *con_reg;
Yadwinder Singh Brar07dc76f2013-06-11 15:01:07 +053020 enum samsung_pll_type type;
Yadwinder Singh Brar3ff6e0d2013-06-11 15:01:12 +053021 unsigned int rate_count;
22 const struct samsung_pll_rate_table *rate_table;
Yadwinder Singh Brar079dbea2013-06-11 15:01:06 +053023};
24
25#define to_clk_pll(_hw) container_of(_hw, struct samsung_clk_pll, hw)
26
Yadwinder Singh Brardfa89312013-06-11 15:01:13 +053027static const struct samsung_pll_rate_table *samsung_get_pll_settings(
28 struct samsung_clk_pll *pll, unsigned long rate)
29{
30 const struct samsung_pll_rate_table *rate_table = pll->rate_table;
31 int i;
32
33 for (i = 0; i < pll->rate_count; i++) {
34 if (rate == rate_table[i].rate)
35 return &rate_table[i];
36 }
37
38 return NULL;
39}
40
41static long samsung_pll_round_rate(struct clk_hw *hw,
42 unsigned long drate, unsigned long *prate)
43{
44 struct samsung_clk_pll *pll = to_clk_pll(hw);
45 const struct samsung_pll_rate_table *rate_table = pll->rate_table;
46 int i;
47
48 /* Assumming rate_table is in descending order */
49 for (i = 0; i < pll->rate_count; i++) {
50 if (drate >= rate_table[i].rate)
51 return rate_table[i].rate;
52 }
53
54 /* return minimum supported value */
55 return rate_table[i - 1].rate;
56}
57
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +090058/*
59 * PLL35xx Clock Type
60 */
Yadwinder Singh Brardfa89312013-06-11 15:01:13 +053061/* Maximum lock time can be 270 * PDIV cycles */
62#define PLL35XX_LOCK_FACTOR (270)
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +090063
64#define PLL35XX_MDIV_MASK (0x3FF)
65#define PLL35XX_PDIV_MASK (0x3F)
66#define PLL35XX_SDIV_MASK (0x7)
Yadwinder Singh Brardfa89312013-06-11 15:01:13 +053067#define PLL35XX_LOCK_STAT_MASK (0x1)
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +090068#define PLL35XX_MDIV_SHIFT (16)
69#define PLL35XX_PDIV_SHIFT (8)
70#define PLL35XX_SDIV_SHIFT (0)
Yadwinder Singh Brardfa89312013-06-11 15:01:13 +053071#define PLL35XX_LOCK_STAT_SHIFT (29)
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +090072
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +090073static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
74 unsigned long parent_rate)
75{
Yadwinder Singh Brar079dbea2013-06-11 15:01:06 +053076 struct samsung_clk_pll *pll = to_clk_pll(hw);
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +090077 u32 mdiv, pdiv, sdiv, pll_con;
78 u64 fvco = parent_rate;
79
80 pll_con = __raw_readl(pll->con_reg);
81 mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
82 pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
83 sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK;
84
85 fvco *= mdiv;
86 do_div(fvco, (pdiv << sdiv));
87
88 return (unsigned long)fvco;
89}
90
Yadwinder Singh Brardfa89312013-06-11 15:01:13 +053091static inline bool samsung_pll35xx_mp_change(
92 const struct samsung_pll_rate_table *rate, u32 pll_con)
93{
94 u32 old_mdiv, old_pdiv;
95
96 old_mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
97 old_pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
98
99 return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv);
100}
101
102static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate,
103 unsigned long prate)
104{
105 struct samsung_clk_pll *pll = to_clk_pll(hw);
106 const struct samsung_pll_rate_table *rate;
107 u32 tmp;
108
109 /* Get required rate settings from table */
110 rate = samsung_get_pll_settings(pll, drate);
111 if (!rate) {
112 pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
113 drate, __clk_get_name(hw->clk));
114 return -EINVAL;
115 }
116
117 tmp = __raw_readl(pll->con_reg);
118
119 if (!(samsung_pll35xx_mp_change(rate, tmp))) {
120 /* If only s change, change just s value only*/
121 tmp &= ~(PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT);
122 tmp |= rate->sdiv << PLL35XX_SDIV_SHIFT;
123 __raw_writel(tmp, pll->con_reg);
124
125 return 0;
126 }
127
128 /* Set PLL lock time. */
129 __raw_writel(rate->pdiv * PLL35XX_LOCK_FACTOR,
130 pll->lock_reg);
131
132 /* Change PLL PMS values */
133 tmp &= ~((PLL35XX_MDIV_MASK << PLL35XX_MDIV_SHIFT) |
134 (PLL35XX_PDIV_MASK << PLL35XX_PDIV_SHIFT) |
135 (PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT));
136 tmp |= (rate->mdiv << PLL35XX_MDIV_SHIFT) |
137 (rate->pdiv << PLL35XX_PDIV_SHIFT) |
138 (rate->sdiv << PLL35XX_SDIV_SHIFT);
139 __raw_writel(tmp, pll->con_reg);
140
141 /* wait_lock_time */
142 do {
143 cpu_relax();
144 tmp = __raw_readl(pll->con_reg);
145 } while (!(tmp & (PLL35XX_LOCK_STAT_MASK
146 << PLL35XX_LOCK_STAT_SHIFT)));
147 return 0;
148}
149
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900150static const struct clk_ops samsung_pll35xx_clk_ops = {
151 .recalc_rate = samsung_pll35xx_recalc_rate,
Yadwinder Singh Brardfa89312013-06-11 15:01:13 +0530152 .round_rate = samsung_pll_round_rate,
153 .set_rate = samsung_pll35xx_set_rate,
154};
155
156static const struct clk_ops samsung_pll35xx_clk_min_ops = {
157 .recalc_rate = samsung_pll35xx_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900158};
159
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900160/*
161 * PLL36xx Clock Type
162 */
163
164#define PLL36XX_KDIV_MASK (0xFFFF)
165#define PLL36XX_MDIV_MASK (0x1FF)
166#define PLL36XX_PDIV_MASK (0x3F)
167#define PLL36XX_SDIV_MASK (0x7)
168#define PLL36XX_MDIV_SHIFT (16)
169#define PLL36XX_PDIV_SHIFT (8)
170#define PLL36XX_SDIV_SHIFT (0)
171
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900172static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw,
173 unsigned long parent_rate)
174{
Yadwinder Singh Brar079dbea2013-06-11 15:01:06 +0530175 struct samsung_clk_pll *pll = to_clk_pll(hw);
Doug Anderson071ff9a2013-06-11 08:24:05 -0700176 u32 mdiv, pdiv, sdiv, pll_con0, pll_con1;
177 s16 kdiv;
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900178 u64 fvco = parent_rate;
179
180 pll_con0 = __raw_readl(pll->con_reg);
181 pll_con1 = __raw_readl(pll->con_reg + 4);
182 mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK;
183 pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK;
184 sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK;
Doug Anderson071ff9a2013-06-11 08:24:05 -0700185 kdiv = (s16)(pll_con1 & PLL36XX_KDIV_MASK);
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900186
187 fvco *= (mdiv << 16) + kdiv;
188 do_div(fvco, (pdiv << sdiv));
189 fvco >>= 16;
190
191 return (unsigned long)fvco;
192}
193
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900194static const struct clk_ops samsung_pll36xx_clk_ops = {
195 .recalc_rate = samsung_pll36xx_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900196};
197
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900198/*
199 * PLL45xx Clock Type
200 */
201
202#define PLL45XX_MDIV_MASK (0x3FF)
203#define PLL45XX_PDIV_MASK (0x3F)
204#define PLL45XX_SDIV_MASK (0x7)
205#define PLL45XX_MDIV_SHIFT (16)
206#define PLL45XX_PDIV_SHIFT (8)
207#define PLL45XX_SDIV_SHIFT (0)
208
209struct samsung_clk_pll45xx {
210 struct clk_hw hw;
211 enum pll45xx_type type;
212 const void __iomem *con_reg;
213};
214
215#define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw)
216
217static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
218 unsigned long parent_rate)
219{
220 struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw);
221 u32 mdiv, pdiv, sdiv, pll_con;
222 u64 fvco = parent_rate;
223
224 pll_con = __raw_readl(pll->con_reg);
225 mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
226 pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
227 sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
228
229 if (pll->type == pll_4508)
230 sdiv = sdiv - 1;
231
232 fvco *= mdiv;
233 do_div(fvco, (pdiv << sdiv));
234
235 return (unsigned long)fvco;
236}
237
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900238static const struct clk_ops samsung_pll45xx_clk_ops = {
239 .recalc_rate = samsung_pll45xx_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900240};
241
242struct clk * __init samsung_clk_register_pll45xx(const char *name,
243 const char *pname, const void __iomem *con_reg,
244 enum pll45xx_type type)
245{
246 struct samsung_clk_pll45xx *pll;
247 struct clk *clk;
248 struct clk_init_data init;
249
250 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
251 if (!pll) {
252 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
253 return NULL;
254 }
255
256 init.name = name;
257 init.ops = &samsung_pll45xx_clk_ops;
258 init.flags = CLK_GET_RATE_NOCACHE;
259 init.parent_names = &pname;
260 init.num_parents = 1;
261
262 pll->hw.init = &init;
263 pll->con_reg = con_reg;
264 pll->type = type;
265
266 clk = clk_register(NULL, &pll->hw);
267 if (IS_ERR(clk)) {
268 pr_err("%s: failed to register pll clock %s\n", __func__,
269 name);
270 kfree(pll);
271 }
272
273 if (clk_register_clkdev(clk, name, NULL))
274 pr_err("%s: failed to register lookup for %s", __func__, name);
275
276 return clk;
277}
278
279/*
280 * PLL46xx Clock Type
281 */
282
283#define PLL46XX_MDIV_MASK (0x1FF)
284#define PLL46XX_PDIV_MASK (0x3F)
285#define PLL46XX_SDIV_MASK (0x7)
286#define PLL46XX_MDIV_SHIFT (16)
287#define PLL46XX_PDIV_SHIFT (8)
288#define PLL46XX_SDIV_SHIFT (0)
289
290#define PLL46XX_KDIV_MASK (0xFFFF)
291#define PLL4650C_KDIV_MASK (0xFFF)
292#define PLL46XX_KDIV_SHIFT (0)
293
294struct samsung_clk_pll46xx {
295 struct clk_hw hw;
296 enum pll46xx_type type;
297 const void __iomem *con_reg;
298};
299
300#define to_clk_pll46xx(_hw) container_of(_hw, struct samsung_clk_pll46xx, hw)
301
302static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
303 unsigned long parent_rate)
304{
305 struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw);
306 u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift;
307 u64 fvco = parent_rate;
308
309 pll_con0 = __raw_readl(pll->con_reg);
310 pll_con1 = __raw_readl(pll->con_reg + 4);
311 mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
312 pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
313 sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
314 kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK :
315 pll_con1 & PLL46XX_KDIV_MASK;
316
317 shift = pll->type == pll_4600 ? 16 : 10;
318 fvco *= (mdiv << shift) + kdiv;
319 do_div(fvco, (pdiv << sdiv));
320 fvco >>= shift;
321
322 return (unsigned long)fvco;
323}
324
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900325static const struct clk_ops samsung_pll46xx_clk_ops = {
326 .recalc_rate = samsung_pll46xx_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900327};
328
329struct clk * __init samsung_clk_register_pll46xx(const char *name,
330 const char *pname, const void __iomem *con_reg,
331 enum pll46xx_type type)
332{
333 struct samsung_clk_pll46xx *pll;
334 struct clk *clk;
335 struct clk_init_data init;
336
337 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
338 if (!pll) {
339 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
340 return NULL;
341 }
342
343 init.name = name;
344 init.ops = &samsung_pll46xx_clk_ops;
345 init.flags = CLK_GET_RATE_NOCACHE;
346 init.parent_names = &pname;
347 init.num_parents = 1;
348
349 pll->hw.init = &init;
350 pll->con_reg = con_reg;
351 pll->type = type;
352
353 clk = clk_register(NULL, &pll->hw);
354 if (IS_ERR(clk)) {
355 pr_err("%s: failed to register pll clock %s\n", __func__,
356 name);
357 kfree(pll);
358 }
359
360 if (clk_register_clkdev(clk, name, NULL))
361 pr_err("%s: failed to register lookup for %s", __func__, name);
362
363 return clk;
364}
365
366/*
367 * PLL2550x Clock Type
368 */
369
370#define PLL2550X_R_MASK (0x1)
371#define PLL2550X_P_MASK (0x3F)
372#define PLL2550X_M_MASK (0x3FF)
373#define PLL2550X_S_MASK (0x7)
374#define PLL2550X_R_SHIFT (20)
375#define PLL2550X_P_SHIFT (14)
376#define PLL2550X_M_SHIFT (4)
377#define PLL2550X_S_SHIFT (0)
378
379struct samsung_clk_pll2550x {
380 struct clk_hw hw;
381 const void __iomem *reg_base;
382 unsigned long offset;
383};
384
385#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
386
387static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
388 unsigned long parent_rate)
389{
390 struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
391 u32 r, p, m, s, pll_stat;
392 u64 fvco = parent_rate;
393
394 pll_stat = __raw_readl(pll->reg_base + pll->offset * 3);
395 r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
396 if (!r)
397 return 0;
398 p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK;
399 m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK;
400 s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK;
401
402 fvco *= m;
403 do_div(fvco, (p << s));
404
405 return (unsigned long)fvco;
406}
407
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900408static const struct clk_ops samsung_pll2550x_clk_ops = {
409 .recalc_rate = samsung_pll2550x_recalc_rate,
Thomas Abraham1c4c5fe2013-03-09 17:02:48 +0900410};
411
412struct clk * __init samsung_clk_register_pll2550x(const char *name,
413 const char *pname, const void __iomem *reg_base,
414 const unsigned long offset)
415{
416 struct samsung_clk_pll2550x *pll;
417 struct clk *clk;
418 struct clk_init_data init;
419
420 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
421 if (!pll) {
422 pr_err("%s: could not allocate pll clk %s\n", __func__, name);
423 return NULL;
424 }
425
426 init.name = name;
427 init.ops = &samsung_pll2550x_clk_ops;
428 init.flags = CLK_GET_RATE_NOCACHE;
429 init.parent_names = &pname;
430 init.num_parents = 1;
431
432 pll->hw.init = &init;
433 pll->reg_base = reg_base;
434 pll->offset = offset;
435
436 clk = clk_register(NULL, &pll->hw);
437 if (IS_ERR(clk)) {
438 pr_err("%s: failed to register pll clock %s\n", __func__,
439 name);
440 kfree(pll);
441 }
442
443 if (clk_register_clkdev(clk, name, NULL))
444 pr_err("%s: failed to register lookup for %s", __func__, name);
445
446 return clk;
447}
Yadwinder Singh Brar07dc76f2013-06-11 15:01:07 +0530448
449static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk,
450 void __iomem *base)
451{
452 struct samsung_clk_pll *pll;
453 struct clk *clk;
454 struct clk_init_data init;
Yadwinder Singh Brar3ff6e0d2013-06-11 15:01:12 +0530455 int ret, len;
Yadwinder Singh Brar07dc76f2013-06-11 15:01:07 +0530456
457 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
458 if (!pll) {
459 pr_err("%s: could not allocate pll clk %s\n",
460 __func__, pll_clk->name);
461 return;
462 }
463
464 init.name = pll_clk->name;
465 init.flags = pll_clk->flags;
466 init.parent_names = &pll_clk->parent_name;
467 init.num_parents = 1;
468
Yadwinder Singh Brar3ff6e0d2013-06-11 15:01:12 +0530469 if (pll_clk->rate_table) {
470 /* find count of rates in rate_table */
471 for (len = 0; pll_clk->rate_table[len].rate != 0; )
472 len++;
473
474 pll->rate_count = len;
475 pll->rate_table = kmemdup(pll_clk->rate_table,
476 pll->rate_count *
477 sizeof(struct samsung_pll_rate_table),
478 GFP_KERNEL);
479 WARN(!pll->rate_table,
480 "%s: could not allocate rate table for %s\n",
481 __func__, pll_clk->name);
482 }
483
Yadwinder Singh Brar07dc76f2013-06-11 15:01:07 +0530484 switch (pll_clk->type) {
485 /* clk_ops for 35xx and 2550 are similar */
486 case pll_35xx:
487 case pll_2550:
Yadwinder Singh Brardfa89312013-06-11 15:01:13 +0530488 if (!pll->rate_table)
489 init.ops = &samsung_pll35xx_clk_min_ops;
490 else
491 init.ops = &samsung_pll35xx_clk_ops;
Yadwinder Singh Brar07dc76f2013-06-11 15:01:07 +0530492 break;
493 /* clk_ops for 36xx and 2650 are similar */
494 case pll_36xx:
495 case pll_2650:
496 init.ops = &samsung_pll36xx_clk_ops;
497 break;
498 default:
499 pr_warn("%s: Unknown pll type for pll clk %s\n",
500 __func__, pll_clk->name);
501 }
502
503 pll->hw.init = &init;
504 pll->type = pll_clk->type;
505 pll->lock_reg = base + pll_clk->lock_offset;
506 pll->con_reg = base + pll_clk->con_offset;
507
508 clk = clk_register(NULL, &pll->hw);
509 if (IS_ERR(clk)) {
510 pr_err("%s: failed to register pll clock %s : %ld\n",
511 __func__, pll_clk->name, PTR_ERR(clk));
512 kfree(pll);
513 return;
514 }
515
516 samsung_clk_add_lookup(clk, pll_clk->id);
517
518 if (!pll_clk->alias)
519 return;
520
521 ret = clk_register_clkdev(clk, pll_clk->alias, pll_clk->dev_name);
522 if (ret)
523 pr_err("%s: failed to register lookup for %s : %d",
524 __func__, pll_clk->name, ret);
525}
526
527void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
528 unsigned int nr_pll, void __iomem *base)
529{
530 int cnt;
531
532 for (cnt = 0; cnt < nr_pll; cnt++)
533 _samsung_clk_register_pll(&pll_list[cnt], base);
534}