blob: 7ed1d4e8ae0e7d62cb4c749242d451585709e8a2 [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
Thomas Abraham0fe967a2010-05-17 09:38:37 +090090static struct clksrc_clk clk_sclk_a2m = {
91 .clk = {
92 .name = "sclk_a2m",
93 .id = -1,
94 .parent = &clk_mout_apll.clk,
95 },
96 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 4, .size = 3 },
97};
98
99static struct clk *clkset_hclk_sys_list[] = {
100 [0] = &clk_mout_mpll.clk,
101 [1] = &clk_sclk_a2m.clk,
102};
103
104static struct clksrc_sources clkset_hclk_sys = {
105 .sources = clkset_hclk_sys_list,
106 .nr_sources = ARRAY_SIZE(clkset_hclk_sys_list),
107};
108
109static struct clksrc_clk clk_hclk_dsys = {
110 .clk = {
111 .name = "hclk_dsys",
112 .id = -1,
113 },
114 .sources = &clkset_hclk_sys,
115 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 20, .size = 1 },
116 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 16, .size = 4 },
117};
118
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900119static struct clksrc_clk clk_hclk_psys = {
120 .clk = {
121 .name = "hclk_psys",
122 .id = -1,
123 },
124 .sources = &clkset_hclk_sys,
125 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 24, .size = 1 },
126 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 24, .size = 4 },
127};
128
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900129static int s5pv210_clk_ip0_ctrl(struct clk *clk, int enable)
130{
131 return s5p_gatectrl(S5P_CLKGATE_IP0, clk, enable);
132}
133
134static int s5pv210_clk_ip1_ctrl(struct clk *clk, int enable)
135{
136 return s5p_gatectrl(S5P_CLKGATE_IP1, clk, enable);
137}
138
139static int s5pv210_clk_ip2_ctrl(struct clk *clk, int enable)
140{
141 return s5p_gatectrl(S5P_CLKGATE_IP2, clk, enable);
142}
143
144static int s5pv210_clk_ip3_ctrl(struct clk *clk, int enable)
145{
146 return s5p_gatectrl(S5P_CLKGATE_IP3, clk, enable);
147}
148
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900149static struct clk clk_h100 = {
150 .name = "hclk100",
151 .id = -1,
152};
153
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900154static struct clk clk_p100 = {
155 .name = "pclk100",
156 .id = -1,
157};
158
159static struct clk clk_p83 = {
160 .name = "pclk83",
161 .id = -1,
162};
163
164static struct clk clk_p66 = {
165 .name = "pclk66",
166 .id = -1,
167};
168
169static struct clk *sys_clks[] = {
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900170 &clk_h100,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900171 &clk_p100,
172 &clk_p83,
173 &clk_p66
174};
175
176static struct clk init_clocks_disable[] = {
177 {
178 .name = "rot",
179 .id = -1,
Thomas Abraham0fe967a2010-05-17 09:38:37 +0900180 .parent = &clk_hclk_dsys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900181 .enable = s5pv210_clk_ip0_ctrl,
182 .ctrlbit = (1<<29),
183 }, {
184 .name = "otg",
185 .id = -1,
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900186 .parent = &clk_hclk_psys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900187 .enable = s5pv210_clk_ip1_ctrl,
188 .ctrlbit = (1<<16),
189 }, {
190 .name = "usb-host",
191 .id = -1,
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900192 .parent = &clk_hclk_psys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900193 .enable = s5pv210_clk_ip1_ctrl,
194 .ctrlbit = (1<<17),
195 }, {
196 .name = "lcd",
197 .id = -1,
Thomas Abraham0fe967a2010-05-17 09:38:37 +0900198 .parent = &clk_hclk_dsys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900199 .enable = s5pv210_clk_ip1_ctrl,
200 .ctrlbit = (1<<0),
201 }, {
202 .name = "cfcon",
203 .id = 0,
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900204 .parent = &clk_hclk_psys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900205 .enable = s5pv210_clk_ip1_ctrl,
206 .ctrlbit = (1<<25),
207 }, {
208 .name = "hsmmc",
209 .id = 0,
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900210 .parent = &clk_hclk_psys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900211 .enable = s5pv210_clk_ip2_ctrl,
212 .ctrlbit = (1<<16),
213 }, {
214 .name = "hsmmc",
215 .id = 1,
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900216 .parent = &clk_hclk_psys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900217 .enable = s5pv210_clk_ip2_ctrl,
218 .ctrlbit = (1<<17),
219 }, {
220 .name = "hsmmc",
221 .id = 2,
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900222 .parent = &clk_hclk_psys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900223 .enable = s5pv210_clk_ip2_ctrl,
224 .ctrlbit = (1<<18),
225 }, {
226 .name = "hsmmc",
227 .id = 3,
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900228 .parent = &clk_hclk_psys.clk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900229 .enable = s5pv210_clk_ip2_ctrl,
230 .ctrlbit = (1<<19),
231 }, {
232 .name = "systimer",
233 .id = -1,
234 .parent = &clk_p66,
235 .enable = s5pv210_clk_ip3_ctrl,
236 .ctrlbit = (1<<16),
237 }, {
238 .name = "watchdog",
239 .id = -1,
240 .parent = &clk_p66,
241 .enable = s5pv210_clk_ip3_ctrl,
242 .ctrlbit = (1<<22),
243 }, {
244 .name = "rtc",
245 .id = -1,
246 .parent = &clk_p66,
247 .enable = s5pv210_clk_ip3_ctrl,
248 .ctrlbit = (1<<15),
249 }, {
250 .name = "i2c",
251 .id = 0,
252 .parent = &clk_p66,
253 .enable = s5pv210_clk_ip3_ctrl,
254 .ctrlbit = (1<<7),
255 }, {
256 .name = "i2c",
257 .id = 1,
258 .parent = &clk_p66,
259 .enable = s5pv210_clk_ip3_ctrl,
260 .ctrlbit = (1<<8),
261 }, {
262 .name = "i2c",
263 .id = 2,
264 .parent = &clk_p66,
265 .enable = s5pv210_clk_ip3_ctrl,
266 .ctrlbit = (1<<9),
267 }, {
268 .name = "spi",
269 .id = 0,
270 .parent = &clk_p66,
271 .enable = s5pv210_clk_ip3_ctrl,
272 .ctrlbit = (1<<12),
273 }, {
274 .name = "spi",
275 .id = 1,
276 .parent = &clk_p66,
277 .enable = s5pv210_clk_ip3_ctrl,
278 .ctrlbit = (1<<13),
279 }, {
280 .name = "spi",
281 .id = 2,
282 .parent = &clk_p66,
283 .enable = s5pv210_clk_ip3_ctrl,
284 .ctrlbit = (1<<14),
285 }, {
286 .name = "timers",
287 .id = -1,
288 .parent = &clk_p66,
289 .enable = s5pv210_clk_ip3_ctrl,
290 .ctrlbit = (1<<23),
291 }, {
292 .name = "adc",
293 .id = -1,
294 .parent = &clk_p66,
295 .enable = s5pv210_clk_ip3_ctrl,
296 .ctrlbit = (1<<24),
297 }, {
298 .name = "keypad",
299 .id = -1,
300 .parent = &clk_p66,
301 .enable = s5pv210_clk_ip3_ctrl,
302 .ctrlbit = (1<<21),
303 }, {
304 .name = "i2s_v50",
305 .id = 0,
306 .parent = &clk_p,
307 .enable = s5pv210_clk_ip3_ctrl,
308 .ctrlbit = (1<<4),
309 }, {
310 .name = "i2s_v32",
311 .id = 0,
312 .parent = &clk_p,
313 .enable = s5pv210_clk_ip3_ctrl,
314 .ctrlbit = (1<<4),
315 }, {
316 .name = "i2s_v32",
317 .id = 1,
318 .parent = &clk_p,
319 .enable = s5pv210_clk_ip3_ctrl,
320 .ctrlbit = (1<<4),
321 }
322};
323
324static struct clk init_clocks[] = {
325 {
326 .name = "uart",
327 .id = 0,
328 .parent = &clk_p66,
329 .enable = s5pv210_clk_ip3_ctrl,
330 .ctrlbit = (1<<7),
331 }, {
332 .name = "uart",
333 .id = 1,
334 .parent = &clk_p66,
335 .enable = s5pv210_clk_ip3_ctrl,
336 .ctrlbit = (1<<8),
337 }, {
338 .name = "uart",
339 .id = 2,
340 .parent = &clk_p66,
341 .enable = s5pv210_clk_ip3_ctrl,
342 .ctrlbit = (1<<9),
343 }, {
344 .name = "uart",
345 .id = 3,
346 .parent = &clk_p66,
347 .enable = s5pv210_clk_ip3_ctrl,
348 .ctrlbit = (1<<10),
349 },
350};
351
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900352static struct clk *clkset_uart_list[] = {
353 [6] = &clk_mout_mpll.clk,
354 [7] = &clk_mout_epll.clk,
355};
356
357static struct clksrc_sources clkset_uart = {
358 .sources = clkset_uart_list,
359 .nr_sources = ARRAY_SIZE(clkset_uart_list),
360};
361
362static struct clksrc_clk clksrcs[] = {
363 {
364 .clk = {
365 .name = "uclk1",
366 .id = -1,
367 .ctrlbit = (1<<17),
368 .enable = s5pv210_clk_ip3_ctrl,
369 },
370 .sources = &clkset_uart,
371 .reg_src = { .reg = S5P_CLK_SRC4, .shift = 16, .size = 4 },
372 .reg_div = { .reg = S5P_CLK_DIV4, .shift = 16, .size = 4 },
373 }
374};
375
376/* Clock initialisation code */
Thomas Abrahameb1ef1e2010-05-17 09:38:12 +0900377static struct clksrc_clk *sysclks[] = {
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900378 &clk_mout_apll,
379 &clk_mout_epll,
380 &clk_mout_mpll,
Thomas Abraham374e0bf2010-05-17 09:38:31 +0900381 &clk_armclk,
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900382 &clk_hclk_msys,
Thomas Abraham0fe967a2010-05-17 09:38:37 +0900383 &clk_sclk_a2m,
384 &clk_hclk_dsys,
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900385 &clk_hclk_psys,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900386};
387
388#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
389
390void __init_or_cpufreq s5pv210_setup_clocks(void)
391{
392 struct clk *xtal_clk;
393 unsigned long xtal;
394 unsigned long armclk;
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900395 unsigned long hclk_msys;
Thomas Abraham0fe967a2010-05-17 09:38:37 +0900396 unsigned long hclk_dsys;
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900397 unsigned long hclk_psys;
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900398 unsigned long pclk100;
399 unsigned long pclk83;
400 unsigned long pclk66;
401 unsigned long apll;
402 unsigned long mpll;
403 unsigned long epll;
404 unsigned int ptr;
405 u32 clkdiv0, clkdiv1;
406
407 printk(KERN_DEBUG "%s: registering clocks\n", __func__);
408
409 clkdiv0 = __raw_readl(S5P_CLK_DIV0);
410 clkdiv1 = __raw_readl(S5P_CLK_DIV1);
411
412 printk(KERN_DEBUG "%s: clkdiv0 = %08x, clkdiv1 = %08x\n",
413 __func__, clkdiv0, clkdiv1);
414
415 xtal_clk = clk_get(NULL, "xtal");
416 BUG_ON(IS_ERR(xtal_clk));
417
418 xtal = clk_get_rate(xtal_clk);
419 clk_put(xtal_clk);
420
421 printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
422
423 apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON), pll_4508);
424 mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON), pll_4502);
425 epll = s5p_get_pll45xx(xtal, __raw_readl(S5P_EPLL_CON), pll_4500);
426
Thomas Abrahamc62ec6a2010-05-17 09:38:28 +0900427 clk_fout_apll.rate = apll;
428 clk_fout_mpll.rate = mpll;
429 clk_fout_epll.rate = epll;
430
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900431 printk(KERN_INFO "S5PV210: PLL settings, A=%ld, M=%ld, E=%ld",
432 apll, mpll, epll);
433
Thomas Abraham374e0bf2010-05-17 09:38:31 +0900434 armclk = clk_get_rate(&clk_armclk.clk);
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900435 hclk_msys = clk_get_rate(&clk_hclk_msys.clk);
Thomas Abraham0fe967a2010-05-17 09:38:37 +0900436 hclk_dsys = clk_get_rate(&clk_hclk_dsys.clk);
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900437 hclk_psys = clk_get_rate(&clk_hclk_psys.clk);
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900438
Thomas Abrahamaf76a202010-05-17 09:38:34 +0900439 pclk100 = hclk_msys / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK100);
Thomas Abraham0fe967a2010-05-17 09:38:37 +0900440 pclk83 = hclk_dsys / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK83);
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900441 pclk66 = hclk_psys / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK66);
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900442
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900443 printk(KERN_INFO "S5PV210: ARMCLK=%ld, HCLKM=%ld, HCLKD=%ld\n"
444 "HCLKP=%ld, PCLKM=%ld, PCLKD=%ld, PCLKP=%ld\n",
445 armclk, hclk_msys, hclk_dsys, hclk_psys,
446 pclk100, pclk83, pclk66);
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900447
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900448 clk_f.rate = armclk;
Thomas Abrahamacfa2452010-05-17 09:38:40 +0900449 clk_h.rate = hclk_psys;
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900450 clk_p.rate = pclk66;
451 clk_p66.rate = pclk66;
452 clk_p83.rate = pclk83;
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900453
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900454 for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++)
455 s3c_set_clksrc(&clksrcs[ptr], true);
456}
457
458static struct clk *clks[] __initdata = {
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900459};
460
461void __init s5pv210_register_clocks(void)
462{
463 struct clk *clkp;
464 int ret;
465 int ptr;
466
467 ret = s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
468 if (ret > 0)
469 printk(KERN_ERR "Failed to register %u clocks\n", ret);
470
Thomas Abrahameb1ef1e2010-05-17 09:38:12 +0900471 for (ptr = 0; ptr < ARRAY_SIZE(sysclks); ptr++)
472 s3c_register_clksrc(sysclks[ptr], 1);
473
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900474 s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
475 s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
476
477 ret = s3c24xx_register_clocks(sys_clks, ARRAY_SIZE(sys_clks));
478 if (ret > 0)
479 printk(KERN_ERR "Failed to register system clocks\n");
480
481 clkp = init_clocks_disable;
482 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
483 ret = s3c24xx_register_clock(clkp);
484 if (ret < 0) {
485 printk(KERN_ERR "Failed to register clock %s (%d)\n",
486 clkp->name, ret);
487 }
488 (clkp->enable)(clkp, 0);
489 }
490
491 s3c_pwmclk_init();
492}