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