• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
27 struct 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 
samsung_pll35xx_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)34 static 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 static const struct clk_ops samsung_pll35xx_clk_ops = {
53 	.recalc_rate = samsung_pll35xx_recalc_rate,
54 };
55 
samsung_clk_register_pll35xx(const char * name,const char * pname,const void __iomem * con_reg)56 struct 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 
103 struct 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 
samsung_pll36xx_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)110 static 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);
114 	u32 mdiv, pdiv, sdiv, pll_con0, pll_con1;
115 	s16 kdiv;
116 	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;
123 	kdiv = (s16)(pll_con1 & PLL36XX_KDIV_MASK);
124 
125 	fvco *= (mdiv << 16) + kdiv;
126 	do_div(fvco, (pdiv << sdiv));
127 	fvco >>= 16;
128 
129 	return (unsigned long)fvco;
130 }
131 
132 static const struct clk_ops samsung_pll36xx_clk_ops = {
133 	.recalc_rate = samsung_pll36xx_recalc_rate,
134 };
135 
samsung_clk_register_pll36xx(const char * name,const char * pname,const void __iomem * con_reg)136 struct 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 
182 struct 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 
samsung_pll45xx_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)190 static 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 
211 static const struct clk_ops samsung_pll45xx_clk_ops = {
212 	.recalc_rate = samsung_pll45xx_recalc_rate,
213 };
214 
samsung_clk_register_pll45xx(const char * name,const char * pname,const void __iomem * con_reg,enum pll45xx_type type)215 struct 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 
267 struct 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 
samsung_pll46xx_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)275 static 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 
298 static const struct clk_ops samsung_pll46xx_clk_ops = {
299 	.recalc_rate = samsung_pll46xx_recalc_rate,
300 };
301 
samsung_clk_register_pll46xx(const char * name,const char * pname,const void __iomem * con_reg,enum pll46xx_type type)302 struct 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 /*
340  * PLL2550x Clock Type
341  */
342 
343 #define PLL2550X_R_MASK       (0x1)
344 #define PLL2550X_P_MASK       (0x3F)
345 #define PLL2550X_M_MASK       (0x3FF)
346 #define PLL2550X_S_MASK       (0x7)
347 #define PLL2550X_R_SHIFT      (20)
348 #define PLL2550X_P_SHIFT      (14)
349 #define PLL2550X_M_SHIFT      (4)
350 #define PLL2550X_S_SHIFT      (0)
351 
352 struct samsung_clk_pll2550x {
353 	struct clk_hw		hw;
354 	const void __iomem	*reg_base;
355 	unsigned long		offset;
356 };
357 
358 #define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
359 
samsung_pll2550x_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)360 static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
361 				unsigned long parent_rate)
362 {
363 	struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
364 	u32 r, p, m, s, pll_stat;
365 	u64 fvco = parent_rate;
366 
367 	pll_stat = __raw_readl(pll->reg_base + pll->offset * 3);
368 	r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
369 	if (!r)
370 		return 0;
371 	p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK;
372 	m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK;
373 	s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK;
374 
375 	fvco *= m;
376 	do_div(fvco, (p << s));
377 
378 	return (unsigned long)fvco;
379 }
380 
381 static const struct clk_ops samsung_pll2550x_clk_ops = {
382 	.recalc_rate = samsung_pll2550x_recalc_rate,
383 };
384 
samsung_clk_register_pll2550x(const char * name,const char * pname,const void __iomem * reg_base,const unsigned long offset)385 struct clk * __init samsung_clk_register_pll2550x(const char *name,
386 			const char *pname, const void __iomem *reg_base,
387 			const unsigned long offset)
388 {
389 	struct samsung_clk_pll2550x *pll;
390 	struct clk *clk;
391 	struct clk_init_data init;
392 
393 	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
394 	if (!pll) {
395 		pr_err("%s: could not allocate pll clk %s\n", __func__, name);
396 		return NULL;
397 	}
398 
399 	init.name = name;
400 	init.ops = &samsung_pll2550x_clk_ops;
401 	init.flags = CLK_GET_RATE_NOCACHE;
402 	init.parent_names = &pname;
403 	init.num_parents = 1;
404 
405 	pll->hw.init = &init;
406 	pll->reg_base = reg_base;
407 	pll->offset = offset;
408 
409 	clk = clk_register(NULL, &pll->hw);
410 	if (IS_ERR(clk)) {
411 		pr_err("%s: failed to register pll clock %s\n", __func__,
412 				name);
413 		kfree(pll);
414 	}
415 
416 	if (clk_register_clkdev(clk, name, NULL))
417 		pr_err("%s: failed to register lookup for %s", __func__, name);
418 
419 	return clk;
420 }
421