blob: d5acd261795c7e0a77bf5beab16f545019ed4d7d [file] [log] [blame]
Kukjin Kim0c1945d2010-02-24 16:40:36 +09001/* linux/arch/arm/mach-s5pv210/clock.c
2 *
3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
5 *
6 * S5PV210 - Clock support
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11*/
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/list.h>
17#include <linux/errno.h>
18#include <linux/err.h>
19#include <linux/clk.h>
20#include <linux/sysdev.h>
21#include <linux/io.h>
22
23#include <mach/map.h>
24
25#include <plat/cpu-freq.h>
26#include <mach/regs-clock.h>
27#include <plat/clock.h>
28#include <plat/cpu.h>
29#include <plat/pll.h>
30#include <plat/s5p-clock.h>
31#include <plat/clock-clksrc.h>
32#include <plat/s5pv210.h>
33
Thomas Abraham59cda522010-05-17 09:38:01 +090034static struct clksrc_clk clk_mout_apll = {
35 .clk = {
36 .name = "mout_apll",
37 .id = -1,
38 },
39 .sources = &clk_src_apll,
40 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 0, .size = 1 },
41};
42
43static struct clksrc_clk clk_mout_epll = {
44 .clk = {
45 .name = "mout_epll",
46 .id = -1,
47 },
48 .sources = &clk_src_epll,
49 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 8, .size = 1 },
50};
51
52static struct clksrc_clk clk_mout_mpll = {
53 .clk = {
54 .name = "mout_mpll",
55 .id = -1,
56 },
57 .sources = &clk_src_mpll,
58 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 4, .size = 1 },
59};
60
Thomas Abraham374e0bf2010-05-17 09:38:31 +090061static struct clk *clkset_armclk_list[] = {
62 [0] = &clk_mout_apll.clk,
63 [1] = &clk_mout_mpll.clk,
64};
65
66static struct clksrc_sources clkset_armclk = {
67 .sources = clkset_armclk_list,
68 .nr_sources = ARRAY_SIZE(clkset_armclk_list),
69};
70
71static struct clksrc_clk clk_armclk = {
72 .clk = {
73 .name = "armclk",
74 .id = -1,
75 },
76 .sources = &clkset_armclk,
77 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 16, .size = 1 },
78 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 0, .size = 3 },
79};
80
Thomas Abrahamaf76a202010-05-17 09:38:34 +090081static struct clksrc_clk clk_hclk_msys = {
82 .clk = {
83 .name = "hclk_msys",
84 .id = -1,
85 .parent = &clk_armclk.clk,
86 },
87 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 8, .size = 3 },
88};
89
Kukjin Kim0c1945d2010-02-24 16:40:36 +090090static int s5pv210_clk_ip0_ctrl(struct clk *clk, int enable)
91{
92 return s5p_gatectrl(S5P_CLKGATE_IP0, clk, enable);
93}
94
95static int s5pv210_clk_ip1_ctrl(struct clk *clk, int enable)
96{
97 return s5p_gatectrl(S5P_CLKGATE_IP1, clk, enable);
98}
99
100static int s5pv210_clk_ip2_ctrl(struct clk *clk, int enable)
101{
102 return s5p_gatectrl(S5P_CLKGATE_IP2, clk, enable);
103}
104
105static int s5pv210_clk_ip3_ctrl(struct clk *clk, int enable)
106{
107 return s5p_gatectrl(S5P_CLKGATE_IP3, clk, enable);
108}
109
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900110static struct clk clk_h100 = {
111 .name = "hclk100",
112 .id = -1,
113};
114
115static struct clk clk_h166 = {
116 .name = "hclk166",
117 .id = -1,
118};
119
120static struct clk clk_h133 = {
121 .name = "hclk133",
122 .id = -1,
123};
124
125static struct clk clk_p100 = {
126 .name = "pclk100",
127 .id = -1,
128};
129
130static struct clk clk_p83 = {
131 .name = "pclk83",
132 .id = -1,
133};
134
135static struct clk clk_p66 = {
136 .name = "pclk66",
137 .id = -1,
138};
139
140static struct clk *sys_clks[] = {
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900141 &clk_h100,
142 &clk_h166,
143 &clk_h133,
144 &clk_p100,
145 &clk_p83,
146 &clk_p66
147};
148
149static struct clk init_clocks_disable[] = {
150 {
151 .name = "rot",
152 .id = -1,
153 .parent = &clk_h166,
154 .enable = s5pv210_clk_ip0_ctrl,
155 .ctrlbit = (1<<29),
156 }, {
157 .name = "otg",
158 .id = -1,
159 .parent = &clk_h133,
160 .enable = s5pv210_clk_ip1_ctrl,
161 .ctrlbit = (1<<16),
162 }, {
163 .name = "usb-host",
164 .id = -1,
165 .parent = &clk_h133,
166 .enable = s5pv210_clk_ip1_ctrl,
167 .ctrlbit = (1<<17),
168 }, {
169 .name = "lcd",
170 .id = -1,
171 .parent = &clk_h166,
172 .enable = s5pv210_clk_ip1_ctrl,
173 .ctrlbit = (1<<0),
174 }, {
175 .name = "cfcon",
176 .id = 0,
177 .parent = &clk_h133,
178 .enable = s5pv210_clk_ip1_ctrl,
179 .ctrlbit = (1<<25),
180 }, {
181 .name = "hsmmc",
182 .id = 0,
183 .parent = &clk_h133,
184 .enable = s5pv210_clk_ip2_ctrl,
185 .ctrlbit = (1<<16),
186 }, {
187 .name = "hsmmc",
188 .id = 1,
189 .parent = &clk_h133,
190 .enable = s5pv210_clk_ip2_ctrl,
191 .ctrlbit = (1<<17),
192 }, {
193 .name = "hsmmc",
194 .id = 2,
195 .parent = &clk_h133,
196 .enable = s5pv210_clk_ip2_ctrl,
197 .ctrlbit = (1<<18),
198 }, {
199 .name = "hsmmc",
200 .id = 3,
201 .parent = &clk_h133,
202 .enable = s5pv210_clk_ip2_ctrl,
203 .ctrlbit = (1<<19),
204 }, {
205 .name = "systimer",
206 .id = -1,
207 .parent = &clk_p66,
208 .enable = s5pv210_clk_ip3_ctrl,
209 .ctrlbit = (1<<16),
210 }, {
211 .name = "watchdog",
212 .id = -1,
213 .parent = &clk_p66,
214 .enable = s5pv210_clk_ip3_ctrl,
215 .ctrlbit = (1<<22),
216 }, {
217 .name = "rtc",
218 .id = -1,
219 .parent = &clk_p66,
220 .enable = s5pv210_clk_ip3_ctrl,
221 .ctrlbit = (1<<15),
222 }, {
223 .name = "i2c",
224 .id = 0,
225 .parent = &clk_p66,
226 .enable = s5pv210_clk_ip3_ctrl,
227 .ctrlbit = (1<<7),
228 }, {
229 .name = "i2c",
230 .id = 1,
231 .parent = &clk_p66,
232 .enable = s5pv210_clk_ip3_ctrl,
233 .ctrlbit = (1<<8),
234 }, {
235 .name = "i2c",
236 .id = 2,
237 .parent = &clk_p66,
238 .enable = s5pv210_clk_ip3_ctrl,
239 .ctrlbit = (1<<9),
240 }, {
241 .name = "spi",
242 .id = 0,
243 .parent = &clk_p66,
244 .enable = s5pv210_clk_ip3_ctrl,
245 .ctrlbit = (1<<12),
246 }, {
247 .name = "spi",
248 .id = 1,
249 .parent = &clk_p66,
250 .enable = s5pv210_clk_ip3_ctrl,
251 .ctrlbit = (1<<13),
252 }, {
253 .name = "spi",
254 .id = 2,
255 .parent = &clk_p66,
256 .enable = s5pv210_clk_ip3_ctrl,
257 .ctrlbit = (1<<14),
258 }, {
259 .name = "timers",
260 .id = -1,
261 .parent = &clk_p66,
262 .enable = s5pv210_clk_ip3_ctrl,
263 .ctrlbit = (1<<23),
264 }, {
265 .name = "adc",
266 .id = -1,
267 .parent = &clk_p66,
268 .enable = s5pv210_clk_ip3_ctrl,
269 .ctrlbit = (1<<24),
270 }, {
271 .name = "keypad",
272 .id = -1,
273 .parent = &clk_p66,
274 .enable = s5pv210_clk_ip3_ctrl,
275 .ctrlbit = (1<<21),
276 }, {
277 .name = "i2s_v50",
278 .id = 0,
279 .parent = &clk_p,
280 .enable = s5pv210_clk_ip3_ctrl,
281 .ctrlbit = (1<<4),
282 }, {
283 .name = "i2s_v32",
284 .id = 0,
285 .parent = &clk_p,
286 .enable = s5pv210_clk_ip3_ctrl,
287 .ctrlbit = (1<<4),
288 }, {
289 .name = "i2s_v32",
290 .id = 1,
291 .parent = &clk_p,
292 .enable = s5pv210_clk_ip3_ctrl,
293 .ctrlbit = (1<<4),
294 }
295};
296
297static struct clk init_clocks[] = {
298 {
299 .name = "uart",
300 .id = 0,
301 .parent = &clk_p66,
302 .enable = s5pv210_clk_ip3_ctrl,
303 .ctrlbit = (1<<7),
304 }, {
305 .name = "uart",
306 .id = 1,
307 .parent = &clk_p66,
308 .enable = s5pv210_clk_ip3_ctrl,
309 .ctrlbit = (1<<8),
310 }, {
311 .name = "uart",
312 .id = 2,
313 .parent = &clk_p66,
314 .enable = s5pv210_clk_ip3_ctrl,
315 .ctrlbit = (1<<9),
316 }, {
317 .name = "uart",
318 .id = 3,
319 .parent = &clk_p66,
320 .enable = s5pv210_clk_ip3_ctrl,
321 .ctrlbit = (1<<10),
322 },
323};
324
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900325static struct clk *clkset_uart_list[] = {
326 [6] = &clk_mout_mpll.clk,
327 [7] = &clk_mout_epll.clk,
328};
329
330static struct clksrc_sources clkset_uart = {
331 .sources = clkset_uart_list,
332 .nr_sources = ARRAY_SIZE(clkset_uart_list),
333};
334
335static struct clksrc_clk clksrcs[] = {
336 {
337 .clk = {
338 .name = "uclk1",
339 .id = -1,
340 .ctrlbit = (1<<17),
341 .enable = s5pv210_clk_ip3_ctrl,
342 },
343 .sources = &clkset_uart,
344 .reg_src = { .reg = S5P_CLK_SRC4, .shift = 16, .size = 4 },
345 .reg_div = { .reg = S5P_CLK_DIV4, .shift = 16, .size = 4 },
346 }
347};
348
349/* Clock initialisation code */
Thomas Abrahameb1ef1e2010-05-17 09:38:12 +0900350static struct clksrc_clk *sysclks[] = {
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900351 &clk_mout_apll,
352 &clk_mout_epll,
353 &clk_mout_mpll,
Thomas Abraham374e0bf2010-05-17 09:38:31 +0900354 &clk_armclk,
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900355 &clk_hclk_msys,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900356};
357
358#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
359
360void __init_or_cpufreq s5pv210_setup_clocks(void)
361{
362 struct clk *xtal_clk;
363 unsigned long xtal;
364 unsigned long armclk;
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900365 unsigned long hclk_msys;
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900366 unsigned long hclk166;
367 unsigned long hclk133;
368 unsigned long pclk100;
369 unsigned long pclk83;
370 unsigned long pclk66;
371 unsigned long apll;
372 unsigned long mpll;
373 unsigned long epll;
374 unsigned int ptr;
375 u32 clkdiv0, clkdiv1;
376
377 printk(KERN_DEBUG "%s: registering clocks\n", __func__);
378
379 clkdiv0 = __raw_readl(S5P_CLK_DIV0);
380 clkdiv1 = __raw_readl(S5P_CLK_DIV1);
381
382 printk(KERN_DEBUG "%s: clkdiv0 = %08x, clkdiv1 = %08x\n",
383 __func__, clkdiv0, clkdiv1);
384
385 xtal_clk = clk_get(NULL, "xtal");
386 BUG_ON(IS_ERR(xtal_clk));
387
388 xtal = clk_get_rate(xtal_clk);
389 clk_put(xtal_clk);
390
391 printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
392
393 apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON), pll_4508);
394 mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON), pll_4502);
395 epll = s5p_get_pll45xx(xtal, __raw_readl(S5P_EPLL_CON), pll_4500);
396
Thomas Abrahamc62ec6a2010-05-17 09:38:28 +0900397 clk_fout_apll.rate = apll;
398 clk_fout_mpll.rate = mpll;
399 clk_fout_epll.rate = epll;
400
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900401 printk(KERN_INFO "S5PV210: PLL settings, A=%ld, M=%ld, E=%ld",
402 apll, mpll, epll);
403
Thomas Abraham374e0bf2010-05-17 09:38:31 +0900404 armclk = clk_get_rate(&clk_armclk.clk);
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900405 hclk_msys = clk_get_rate(&clk_hclk_msys.clk);
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900406
407 if (__raw_readl(S5P_CLK_SRC0) & S5P_CLKSRC0_MUX166_MASK) {
408 hclk166 = apll / GET_DIV(clkdiv0, S5P_CLKDIV0_A2M);
409 hclk166 = hclk166 / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK166);
410 } else
411 hclk166 = mpll / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK166);
412
413 if (__raw_readl(S5P_CLK_SRC0) & S5P_CLKSRC0_MUX133_MASK) {
414 hclk133 = apll / GET_DIV(clkdiv0, S5P_CLKDIV0_A2M);
415 hclk133 = hclk133 / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK133);
416 } else
417 hclk133 = mpll / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK133);
418
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900419 pclk100 = hclk_msys / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK100);
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900420 pclk83 = hclk166 / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK83);
421 pclk66 = hclk133 / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK66);
422
423 printk(KERN_INFO "S5PV210: ARMCLK=%ld, HCLKM=%ld, HCLKD=%ld, \
424 HCLKP=%ld, PCLKM=%ld, PCLKD=%ld, PCLKP=%ld\n",
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900425 armclk, hclk_msys, hclk166, hclk133, pclk100, pclk83, pclk66);
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900426
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900427 clk_f.rate = armclk;
428 clk_h.rate = hclk133;
429 clk_p.rate = pclk66;
430 clk_p66.rate = pclk66;
431 clk_p83.rate = pclk83;
432 clk_h133.rate = hclk133;
433 clk_h166.rate = hclk166;
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900434
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900435 for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++)
436 s3c_set_clksrc(&clksrcs[ptr], true);
437}
438
439static struct clk *clks[] __initdata = {
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900440};
441
442void __init s5pv210_register_clocks(void)
443{
444 struct clk *clkp;
445 int ret;
446 int ptr;
447
448 ret = s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
449 if (ret > 0)
450 printk(KERN_ERR "Failed to register %u clocks\n", ret);
451
Thomas Abrahameb1ef1e2010-05-17 09:38:12 +0900452 for (ptr = 0; ptr < ARRAY_SIZE(sysclks); ptr++)
453 s3c_register_clksrc(sysclks[ptr], 1);
454
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900455 s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
456 s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
457
458 ret = s3c24xx_register_clocks(sys_clks, ARRAY_SIZE(sys_clks));
459 if (ret > 0)
460 printk(KERN_ERR "Failed to register system clocks\n");
461
462 clkp = init_clocks_disable;
463 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
464 ret = s3c24xx_register_clock(clkp);
465 if (ret < 0) {
466 printk(KERN_ERR "Failed to register clock %s (%d)\n",
467 clkp->name, ret);
468 }
469 (clkp->enable)(clkp, 0);
470 }
471
472 s3c_pwmclk_init();
473}