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