xref: /openbmc/u-boot/arch/arm/mach-s5pc1xx/clock.c (revision ad5b5801)
1 /*
2  * Copyright (C) 2009 Samsung Electronics
3  * Minkyu Kang <mk7.kang@samsung.com>
4  * Heungjun Kim <riverful.kim@samsung.com>
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 #include <common.h>
10 #include <asm/io.h>
11 #include <asm/arch/clock.h>
12 #include <asm/arch/clk.h>
13 
14 #define CLK_M	0
15 #define CLK_D	1
16 #define CLK_P	2
17 
18 #ifndef CONFIG_SYS_CLK_FREQ_C100
19 #define CONFIG_SYS_CLK_FREQ_C100	12000000
20 #endif
21 #ifndef CONFIG_SYS_CLK_FREQ_C110
22 #define CONFIG_SYS_CLK_FREQ_C110	24000000
23 #endif
24 
25 /* s5pc110: return pll clock frequency */
26 static unsigned long s5pc100_get_pll_clk(int pllreg)
27 {
28 	struct s5pc100_clock *clk =
29 		(struct s5pc100_clock *)samsung_get_base_clock();
30 	unsigned long r, m, p, s, mask, fout;
31 	unsigned int freq;
32 
33 	switch (pllreg) {
34 	case APLL:
35 		r = readl(&clk->apll_con);
36 		break;
37 	case MPLL:
38 		r = readl(&clk->mpll_con);
39 		break;
40 	case EPLL:
41 		r = readl(&clk->epll_con);
42 		break;
43 	case HPLL:
44 		r = readl(&clk->hpll_con);
45 		break;
46 	default:
47 		printf("Unsupported PLL (%d)\n", pllreg);
48 		return 0;
49 	}
50 
51 	/*
52 	 * APLL_CON: MIDV [25:16]
53 	 * MPLL_CON: MIDV [23:16]
54 	 * EPLL_CON: MIDV [23:16]
55 	 * HPLL_CON: MIDV [23:16]
56 	 */
57 	if (pllreg == APLL)
58 		mask = 0x3ff;
59 	else
60 		mask = 0x0ff;
61 
62 	m = (r >> 16) & mask;
63 
64 	/* PDIV [13:8] */
65 	p = (r >> 8) & 0x3f;
66 	/* SDIV [2:0] */
67 	s = r & 0x7;
68 
69 	/* FOUT = MDIV * FIN / (PDIV * 2^SDIV) */
70 	freq = CONFIG_SYS_CLK_FREQ_C100;
71 	fout = m * (freq / (p * (1 << s)));
72 
73 	return fout;
74 }
75 
76 /* s5pc100: return pll clock frequency */
77 static unsigned long s5pc110_get_pll_clk(int pllreg)
78 {
79 	struct s5pc110_clock *clk =
80 		(struct s5pc110_clock *)samsung_get_base_clock();
81 	unsigned long r, m, p, s, mask, fout;
82 	unsigned int freq;
83 
84 	switch (pllreg) {
85 	case APLL:
86 		r = readl(&clk->apll_con);
87 		break;
88 	case MPLL:
89 		r = readl(&clk->mpll_con);
90 		break;
91 	case EPLL:
92 		r = readl(&clk->epll_con);
93 		break;
94 	case VPLL:
95 		r = readl(&clk->vpll_con);
96 		break;
97 	default:
98 		printf("Unsupported PLL (%d)\n", pllreg);
99 		return 0;
100 	}
101 
102 	/*
103 	 * APLL_CON: MIDV [25:16]
104 	 * MPLL_CON: MIDV [25:16]
105 	 * EPLL_CON: MIDV [24:16]
106 	 * VPLL_CON: MIDV [24:16]
107 	 */
108 	if (pllreg == APLL || pllreg == MPLL)
109 		mask = 0x3ff;
110 	else
111 		mask = 0x1ff;
112 
113 	m = (r >> 16) & mask;
114 
115 	/* PDIV [13:8] */
116 	p = (r >> 8) & 0x3f;
117 	/* SDIV [2:0] */
118 	s = r & 0x7;
119 
120 	freq = CONFIG_SYS_CLK_FREQ_C110;
121 	if (pllreg == APLL) {
122 		if (s < 1)
123 			s = 1;
124 		/* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
125 		fout = m * (freq / (p * (1 << (s - 1))));
126 	} else
127 		/* FOUT = MDIV * FIN / (PDIV * 2^SDIV) */
128 		fout = m * (freq / (p * (1 << s)));
129 
130 	return fout;
131 }
132 
133 /* s5pc110: return ARM clock frequency */
134 static unsigned long s5pc110_get_arm_clk(void)
135 {
136 	struct s5pc110_clock *clk =
137 		(struct s5pc110_clock *)samsung_get_base_clock();
138 	unsigned long div;
139 	unsigned long dout_apll, armclk;
140 	unsigned int apll_ratio;
141 
142 	div = readl(&clk->div0);
143 
144 	/* APLL_RATIO: [2:0] */
145 	apll_ratio = div & 0x7;
146 
147 	dout_apll = get_pll_clk(APLL) / (apll_ratio + 1);
148 	armclk = dout_apll;
149 
150 	return armclk;
151 }
152 
153 /* s5pc100: return ARM clock frequency */
154 static unsigned long s5pc100_get_arm_clk(void)
155 {
156 	struct s5pc100_clock *clk =
157 		(struct s5pc100_clock *)samsung_get_base_clock();
158 	unsigned long div;
159 	unsigned long dout_apll, armclk;
160 	unsigned int apll_ratio, arm_ratio;
161 
162 	div = readl(&clk->div0);
163 
164 	/* ARM_RATIO: [6:4] */
165 	arm_ratio = (div >> 4) & 0x7;
166 	/* APLL_RATIO: [0] */
167 	apll_ratio = div & 0x1;
168 
169 	dout_apll = get_pll_clk(APLL) / (apll_ratio + 1);
170 	armclk = dout_apll / (arm_ratio + 1);
171 
172 	return armclk;
173 }
174 
175 /* s5pc100: return HCLKD0 frequency */
176 static unsigned long get_hclk(void)
177 {
178 	struct s5pc100_clock *clk =
179 		(struct s5pc100_clock *)samsung_get_base_clock();
180 	unsigned long hclkd0;
181 	uint div, d0_bus_ratio;
182 
183 	div = readl(&clk->div0);
184 	/* D0_BUS_RATIO: [10:8] */
185 	d0_bus_ratio = (div >> 8) & 0x7;
186 
187 	hclkd0 = get_arm_clk() / (d0_bus_ratio + 1);
188 
189 	return hclkd0;
190 }
191 
192 /* s5pc100: return PCLKD1 frequency */
193 static unsigned long get_pclkd1(void)
194 {
195 	struct s5pc100_clock *clk =
196 		(struct s5pc100_clock *)samsung_get_base_clock();
197 	unsigned long d1_bus, pclkd1;
198 	uint div, d1_bus_ratio, pclkd1_ratio;
199 
200 	div = readl(&clk->div0);
201 	/* D1_BUS_RATIO: [14:12] */
202 	d1_bus_ratio = (div >> 12) & 0x7;
203 	/* PCLKD1_RATIO: [18:16] */
204 	pclkd1_ratio = (div >> 16) & 0x7;
205 
206 	/* ASYNC Mode */
207 	d1_bus = get_pll_clk(MPLL) / (d1_bus_ratio + 1);
208 	pclkd1 = d1_bus / (pclkd1_ratio + 1);
209 
210 	return pclkd1;
211 }
212 
213 /* s5pc110: return HCLKs frequency */
214 static unsigned long get_hclk_sys(int dom)
215 {
216 	struct s5pc110_clock *clk =
217 		(struct s5pc110_clock *)samsung_get_base_clock();
218 	unsigned long hclk;
219 	unsigned int div;
220 	unsigned int offset;
221 	unsigned int hclk_sys_ratio;
222 
223 	if (dom == CLK_M)
224 		return get_hclk();
225 
226 	div = readl(&clk->div0);
227 
228 	/*
229 	 * HCLK_MSYS_RATIO: [10:8]
230 	 * HCLK_DSYS_RATIO: [19:16]
231 	 * HCLK_PSYS_RATIO: [27:24]
232 	 */
233 	offset = 8 + (dom << 0x3);
234 
235 	hclk_sys_ratio = (div >> offset) & 0xf;
236 
237 	hclk = get_pll_clk(MPLL) / (hclk_sys_ratio + 1);
238 
239 	return hclk;
240 }
241 
242 /* s5pc110: return PCLKs frequency */
243 static unsigned long get_pclk_sys(int dom)
244 {
245 	struct s5pc110_clock *clk =
246 		(struct s5pc110_clock *)samsung_get_base_clock();
247 	unsigned long pclk;
248 	unsigned int div;
249 	unsigned int offset;
250 	unsigned int pclk_sys_ratio;
251 
252 	div = readl(&clk->div0);
253 
254 	/*
255 	 * PCLK_MSYS_RATIO: [14:12]
256 	 * PCLK_DSYS_RATIO: [22:20]
257 	 * PCLK_PSYS_RATIO: [30:28]
258 	 */
259 	offset = 12 + (dom << 0x3);
260 
261 	pclk_sys_ratio = (div >> offset) & 0x7;
262 
263 	pclk = get_hclk_sys(dom) / (pclk_sys_ratio + 1);
264 
265 	return pclk;
266 }
267 
268 /* s5pc110: return peripheral clock frequency */
269 static unsigned long s5pc110_get_pclk(void)
270 {
271 	return get_pclk_sys(CLK_P);
272 }
273 
274 /* s5pc100: return peripheral clock frequency */
275 static unsigned long s5pc100_get_pclk(void)
276 {
277 	return get_pclkd1();
278 }
279 
280 /* s5pc1xx: return uart clock frequency */
281 static unsigned long s5pc1xx_get_uart_clk(int dev_index)
282 {
283 	if (cpu_is_s5pc110())
284 		return s5pc110_get_pclk();
285 	else
286 		return s5pc100_get_pclk();
287 }
288 
289 /* s5pc1xx: return pwm clock frequency */
290 static unsigned long s5pc1xx_get_pwm_clk(void)
291 {
292 	if (cpu_is_s5pc110())
293 		return s5pc110_get_pclk();
294 	else
295 		return s5pc100_get_pclk();
296 }
297 
298 unsigned long get_pll_clk(int pllreg)
299 {
300 	if (cpu_is_s5pc110())
301 		return s5pc110_get_pll_clk(pllreg);
302 	else
303 		return s5pc100_get_pll_clk(pllreg);
304 }
305 
306 unsigned long get_arm_clk(void)
307 {
308 	if (cpu_is_s5pc110())
309 		return s5pc110_get_arm_clk();
310 	else
311 		return s5pc100_get_arm_clk();
312 }
313 
314 unsigned long get_pwm_clk(void)
315 {
316 	return s5pc1xx_get_pwm_clk();
317 }
318 
319 unsigned long get_uart_clk(int dev_index)
320 {
321 	return s5pc1xx_get_uart_clk(dev_index);
322 }
323 
324 void set_mmc_clk(int dev_index, unsigned int div)
325 {
326 	/* Do NOTHING */
327 }
328