1 /* linux/arch/arm/plat-samsung/include/plat/pll.h
2  *
3  * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd.
4  *		http://www.samsung.com/
5  *
6  * Copyright 2008 Openmoko, Inc.
7  * Copyright 2008 Simtec Electronics
8  *	Ben Dooks <ben@simtec.co.uk>
9  *	http://armlinux.simtec.co.uk/
10  *
11  * Samsung PLL codes
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License version 2 as
15  * published by the Free Software Foundation.
16 */
17 
18 #include <asm/div64.h>
19 
20 #define S3C24XX_PLL_MDIV_MASK		(0xFF)
21 #define S3C24XX_PLL_PDIV_MASK		(0x1F)
22 #define S3C24XX_PLL_SDIV_MASK		(0x3)
23 #define S3C24XX_PLL_MDIV_SHIFT		(12)
24 #define S3C24XX_PLL_PDIV_SHIFT		(4)
25 #define S3C24XX_PLL_SDIV_SHIFT		(0)
26 
s3c24xx_get_pll(unsigned int pllval,unsigned int baseclk)27 static inline unsigned int s3c24xx_get_pll(unsigned int pllval,
28 					   unsigned int baseclk)
29 {
30 	unsigned int mdiv, pdiv, sdiv;
31 	uint64_t fvco;
32 
33 	mdiv = (pllval >> S3C24XX_PLL_MDIV_SHIFT) & S3C24XX_PLL_MDIV_MASK;
34 	pdiv = (pllval >> S3C24XX_PLL_PDIV_SHIFT) & S3C24XX_PLL_PDIV_MASK;
35 	sdiv = (pllval >> S3C24XX_PLL_SDIV_SHIFT) & S3C24XX_PLL_SDIV_MASK;
36 
37 	fvco = (uint64_t)baseclk * (mdiv + 8);
38 	do_div(fvco, (pdiv + 2) << sdiv);
39 
40 	return (unsigned int)fvco;
41 }
42 
43 #define S3C2416_PLL_MDIV_MASK		(0x3FF)
44 #define S3C2416_PLL_PDIV_MASK		(0x3F)
45 #define S3C2416_PLL_SDIV_MASK		(0x7)
46 #define S3C2416_PLL_MDIV_SHIFT		(14)
47 #define S3C2416_PLL_PDIV_SHIFT		(5)
48 #define S3C2416_PLL_SDIV_SHIFT		(0)
49 
s3c2416_get_pll(unsigned int pllval,unsigned int baseclk)50 static inline unsigned int s3c2416_get_pll(unsigned int pllval,
51 					   unsigned int baseclk)
52 {
53 	unsigned int mdiv, pdiv, sdiv;
54 	uint64_t fvco;
55 
56 	mdiv = (pllval >> S3C2416_PLL_MDIV_SHIFT) & S3C2416_PLL_MDIV_MASK;
57 	pdiv = (pllval >> S3C2416_PLL_PDIV_SHIFT) & S3C2416_PLL_PDIV_MASK;
58 	sdiv = (pllval >> S3C2416_PLL_SDIV_SHIFT) & S3C2416_PLL_SDIV_MASK;
59 
60 	fvco = (uint64_t)baseclk * mdiv;
61 	do_div(fvco, (pdiv << sdiv));
62 
63 	return (unsigned int)fvco;
64 }
65 
66 #define S3C6400_PLL_MDIV_MASK		(0x3FF)
67 #define S3C6400_PLL_PDIV_MASK		(0x3F)
68 #define S3C6400_PLL_SDIV_MASK		(0x7)
69 #define S3C6400_PLL_MDIV_SHIFT		(16)
70 #define S3C6400_PLL_PDIV_SHIFT		(8)
71 #define S3C6400_PLL_SDIV_SHIFT		(0)
72 
s3c6400_get_pll(unsigned long baseclk,u32 pllcon)73 static inline unsigned long s3c6400_get_pll(unsigned long baseclk,
74 					    u32 pllcon)
75 {
76 	u32 mdiv, pdiv, sdiv;
77 	u64 fvco = baseclk;
78 
79 	mdiv = (pllcon >> S3C6400_PLL_MDIV_SHIFT) & S3C6400_PLL_MDIV_MASK;
80 	pdiv = (pllcon >> S3C6400_PLL_PDIV_SHIFT) & S3C6400_PLL_PDIV_MASK;
81 	sdiv = (pllcon >> S3C6400_PLL_SDIV_SHIFT) & S3C6400_PLL_SDIV_MASK;
82 
83 	fvco *= mdiv;
84 	do_div(fvco, (pdiv << sdiv));
85 
86 	return (unsigned long)fvco;
87 }
88 
89 #define PLL6553X_MDIV_MASK	(0x7F)
90 #define PLL6553X_PDIV_MASK	(0x1F)
91 #define PLL6553X_SDIV_MASK	(0x3)
92 #define PLL6553X_KDIV_MASK	(0xFFFF)
93 #define PLL6553X_MDIV_SHIFT	(16)
94 #define PLL6553X_PDIV_SHIFT	(8)
95 #define PLL6553X_SDIV_SHIFT	(0)
96 
s3c_get_pll6553x(unsigned long baseclk,u32 pll_con0,u32 pll_con1)97 static inline unsigned long s3c_get_pll6553x(unsigned long baseclk,
98 					     u32 pll_con0, u32 pll_con1)
99 {
100 	unsigned long result;
101 	u32 mdiv, pdiv, sdiv, kdiv;
102 	u64 tmp;
103 
104 	mdiv = (pll_con0 >> PLL6553X_MDIV_SHIFT) & PLL6553X_MDIV_MASK;
105 	pdiv = (pll_con0 >> PLL6553X_PDIV_SHIFT) & PLL6553X_PDIV_MASK;
106 	sdiv = (pll_con0 >> PLL6553X_SDIV_SHIFT) & PLL6553X_SDIV_MASK;
107 	kdiv = pll_con1 & PLL6553X_KDIV_MASK;
108 
109 	/*
110 	 * We need to multiple baseclk by mdiv (the integer part) and kdiv
111 	 * which is in 2^16ths, so shift mdiv up (does not overflow) and
112 	 * add kdiv before multiplying. The use of tmp is to avoid any
113 	 * overflows before shifting bac down into result when multipling
114 	 * by the mdiv and kdiv pair.
115 	 */
116 
117 	tmp = baseclk;
118 	tmp *= (mdiv << 16) + kdiv;
119 	do_div(tmp, (pdiv << sdiv));
120 	result = tmp >> 16;
121 
122 	return result;
123 }
124 
125 #define PLL35XX_MDIV_MASK	(0x3FF)
126 #define PLL35XX_PDIV_MASK	(0x3F)
127 #define PLL35XX_SDIV_MASK	(0x7)
128 #define PLL35XX_MDIV_SHIFT	(16)
129 #define PLL35XX_PDIV_SHIFT	(8)
130 #define PLL35XX_SDIV_SHIFT	(0)
131 
s5p_get_pll35xx(unsigned long baseclk,u32 pll_con)132 static inline unsigned long s5p_get_pll35xx(unsigned long baseclk, u32 pll_con)
133 {
134 	u32 mdiv, pdiv, sdiv;
135 	u64 fvco = baseclk;
136 
137 	mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
138 	pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
139 	sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK;
140 
141 	fvco *= mdiv;
142 	do_div(fvco, (pdiv << sdiv));
143 
144 	return (unsigned long)fvco;
145 }
146 
147 #define PLL36XX_KDIV_MASK	(0xFFFF)
148 #define PLL36XX_MDIV_MASK	(0x1FF)
149 #define PLL36XX_PDIV_MASK	(0x3F)
150 #define PLL36XX_SDIV_MASK	(0x7)
151 #define PLL36XX_MDIV_SHIFT	(16)
152 #define PLL36XX_PDIV_SHIFT	(8)
153 #define PLL36XX_SDIV_SHIFT	(0)
154 
s5p_get_pll36xx(unsigned long baseclk,u32 pll_con0,u32 pll_con1)155 static inline unsigned long s5p_get_pll36xx(unsigned long baseclk,
156 					    u32 pll_con0, u32 pll_con1)
157 {
158 	unsigned long result;
159 	u32 mdiv, pdiv, sdiv, kdiv;
160 	u64 tmp;
161 
162 	mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK;
163 	pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK;
164 	sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK;
165 	kdiv = pll_con1 & PLL36XX_KDIV_MASK;
166 
167 	tmp = baseclk;
168 
169 	tmp *= (mdiv << 16) + kdiv;
170 	do_div(tmp, (pdiv << sdiv));
171 	result = tmp >> 16;
172 
173 	return result;
174 }
175 
176 #define PLL45XX_MDIV_MASK	(0x3FF)
177 #define PLL45XX_PDIV_MASK	(0x3F)
178 #define PLL45XX_SDIV_MASK	(0x7)
179 #define PLL45XX_MDIV_SHIFT	(16)
180 #define PLL45XX_PDIV_SHIFT	(8)
181 #define PLL45XX_SDIV_SHIFT	(0)
182 
183 enum pll45xx_type_t {
184 	pll_4500,
185 	pll_4502,
186 	pll_4508
187 };
188 
s5p_get_pll45xx(unsigned long baseclk,u32 pll_con,enum pll45xx_type_t pll_type)189 static inline unsigned long s5p_get_pll45xx(unsigned long baseclk, u32 pll_con,
190 					    enum pll45xx_type_t pll_type)
191 {
192 	u32 mdiv, pdiv, sdiv;
193 	u64 fvco = baseclk;
194 
195 	mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
196 	pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
197 	sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
198 
199 	if (pll_type == pll_4508)
200 		sdiv = sdiv - 1;
201 
202 	fvco *= mdiv;
203 	do_div(fvco, (pdiv << sdiv));
204 
205 	return (unsigned long)fvco;
206 }
207 
208 /* CON0 bit-fields */
209 #define PLL46XX_MDIV_MASK	(0x1FF)
210 #define PLL46XX_PDIV_MASK	(0x3F)
211 #define PLL46XX_SDIV_MASK	(0x7)
212 #define PLL46XX_LOCKED_SHIFT	(29)
213 #define PLL46XX_MDIV_SHIFT	(16)
214 #define PLL46XX_PDIV_SHIFT	(8)
215 #define PLL46XX_SDIV_SHIFT	(0)
216 
217 /* CON1 bit-fields */
218 #define PLL46XX_MRR_MASK	(0x1F)
219 #define PLL46XX_MFR_MASK	(0x3F)
220 #define PLL46XX_KDIV_MASK	(0xFFFF)
221 #define PLL4650C_KDIV_MASK	(0xFFF)
222 #define PLL46XX_MRR_SHIFT	(24)
223 #define PLL46XX_MFR_SHIFT	(16)
224 #define PLL46XX_KDIV_SHIFT	(0)
225 
226 enum pll46xx_type_t {
227 	pll_4600,
228 	pll_4650,
229 	pll_4650c,
230 };
231 
s5p_get_pll46xx(unsigned long baseclk,u32 pll_con0,u32 pll_con1,enum pll46xx_type_t pll_type)232 static inline unsigned long s5p_get_pll46xx(unsigned long baseclk,
233 					    u32 pll_con0, u32 pll_con1,
234 					    enum pll46xx_type_t pll_type)
235 {
236 	unsigned long result;
237 	u32 mdiv, pdiv, sdiv, kdiv;
238 	u64 tmp;
239 
240 	mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
241 	pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
242 	sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
243 	kdiv = pll_con1 & PLL46XX_KDIV_MASK;
244 
245 	if (pll_type == pll_4650c)
246 		kdiv = pll_con1 & PLL4650C_KDIV_MASK;
247 	else
248 		kdiv = pll_con1 & PLL46XX_KDIV_MASK;
249 
250 	tmp = baseclk;
251 
252 	if (pll_type == pll_4600) {
253 		tmp *= (mdiv << 16) + kdiv;
254 		do_div(tmp, (pdiv << sdiv));
255 		result = tmp >> 16;
256 	} else {
257 		tmp *= (mdiv << 10) + kdiv;
258 		do_div(tmp, (pdiv << sdiv));
259 		result = tmp >> 10;
260 	}
261 
262 	return result;
263 }
264 
265 #define PLL90XX_MDIV_MASK	(0xFF)
266 #define PLL90XX_PDIV_MASK	(0x3F)
267 #define PLL90XX_SDIV_MASK	(0x7)
268 #define PLL90XX_KDIV_MASK	(0xffff)
269 #define PLL90XX_LOCKED_SHIFT	(29)
270 #define PLL90XX_MDIV_SHIFT	(16)
271 #define PLL90XX_PDIV_SHIFT	(8)
272 #define PLL90XX_SDIV_SHIFT	(0)
273 #define PLL90XX_KDIV_SHIFT	(0)
274 
s5p_get_pll90xx(unsigned long baseclk,u32 pll_con,u32 pll_conk)275 static inline unsigned long s5p_get_pll90xx(unsigned long baseclk,
276 					    u32 pll_con, u32 pll_conk)
277 {
278 	unsigned long result;
279 	u32 mdiv, pdiv, sdiv, kdiv;
280 	u64 tmp;
281 
282 	mdiv = (pll_con >> PLL90XX_MDIV_SHIFT) & PLL90XX_MDIV_MASK;
283 	pdiv = (pll_con >> PLL90XX_PDIV_SHIFT) & PLL90XX_PDIV_MASK;
284 	sdiv = (pll_con >> PLL90XX_SDIV_SHIFT) & PLL90XX_SDIV_MASK;
285 	kdiv = pll_conk & PLL90XX_KDIV_MASK;
286 
287 	/*
288 	 * We need to multiple baseclk by mdiv (the integer part) and kdiv
289 	 * which is in 2^16ths, so shift mdiv up (does not overflow) and
290 	 * add kdiv before multiplying. The use of tmp is to avoid any
291 	 * overflows before shifting bac down into result when multipling
292 	 * by the mdiv and kdiv pair.
293 	 */
294 
295 	tmp = baseclk;
296 	tmp *= (mdiv << 16) + kdiv;
297 	do_div(tmp, (pdiv << sdiv));
298 	result = tmp >> 16;
299 
300 	return result;
301 }
302 
303 #define PLL65XX_MDIV_MASK	(0x3FF)
304 #define PLL65XX_PDIV_MASK	(0x3F)
305 #define PLL65XX_SDIV_MASK	(0x7)
306 #define PLL65XX_MDIV_SHIFT	(16)
307 #define PLL65XX_PDIV_SHIFT	(8)
308 #define PLL65XX_SDIV_SHIFT	(0)
309 
s5p_get_pll65xx(unsigned long baseclk,u32 pll_con)310 static inline unsigned long s5p_get_pll65xx(unsigned long baseclk, u32 pll_con)
311 {
312 	u32 mdiv, pdiv, sdiv;
313 	u64 fvco = baseclk;
314 
315 	mdiv = (pll_con >> PLL65XX_MDIV_SHIFT) & PLL65XX_MDIV_MASK;
316 	pdiv = (pll_con >> PLL65XX_PDIV_SHIFT) & PLL65XX_PDIV_MASK;
317 	sdiv = (pll_con >> PLL65XX_SDIV_SHIFT) & PLL65XX_SDIV_MASK;
318 
319 	fvco *= mdiv;
320 	do_div(fvco, (pdiv << sdiv));
321 
322 	return (unsigned long)fvco;
323 }
324