blob: f57fa1ee6ff01be55a05d59fd3c967c57e36ef9c [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
Kukjin Kim0c1945d2010-02-24 16:40:36 +090081static int s5pv210_clk_ip0_ctrl(struct clk *clk, int enable)
82{
83 return s5p_gatectrl(S5P_CLKGATE_IP0, clk, enable);
84}
85
86static int s5pv210_clk_ip1_ctrl(struct clk *clk, int enable)
87{
88 return s5p_gatectrl(S5P_CLKGATE_IP1, clk, enable);
89}
90
91static int s5pv210_clk_ip2_ctrl(struct clk *clk, int enable)
92{
93 return s5p_gatectrl(S5P_CLKGATE_IP2, clk, enable);
94}
95
96static int s5pv210_clk_ip3_ctrl(struct clk *clk, int enable)
97{
98 return s5p_gatectrl(S5P_CLKGATE_IP3, clk, enable);
99}
100
101static struct clk clk_h200 = {
102 .name = "hclk200",
103 .id = -1,
104};
105
106static struct clk clk_h100 = {
107 .name = "hclk100",
108 .id = -1,
109};
110
111static struct clk clk_h166 = {
112 .name = "hclk166",
113 .id = -1,
114};
115
116static struct clk clk_h133 = {
117 .name = "hclk133",
118 .id = -1,
119};
120
121static struct clk clk_p100 = {
122 .name = "pclk100",
123 .id = -1,
124};
125
126static struct clk clk_p83 = {
127 .name = "pclk83",
128 .id = -1,
129};
130
131static struct clk clk_p66 = {
132 .name = "pclk66",
133 .id = -1,
134};
135
136static struct clk *sys_clks[] = {
137 &clk_h200,
138 &clk_h100,
139 &clk_h166,
140 &clk_h133,
141 &clk_p100,
142 &clk_p83,
143 &clk_p66
144};
145
146static struct clk init_clocks_disable[] = {
147 {
148 .name = "rot",
149 .id = -1,
150 .parent = &clk_h166,
151 .enable = s5pv210_clk_ip0_ctrl,
152 .ctrlbit = (1<<29),
153 }, {
154 .name = "otg",
155 .id = -1,
156 .parent = &clk_h133,
157 .enable = s5pv210_clk_ip1_ctrl,
158 .ctrlbit = (1<<16),
159 }, {
160 .name = "usb-host",
161 .id = -1,
162 .parent = &clk_h133,
163 .enable = s5pv210_clk_ip1_ctrl,
164 .ctrlbit = (1<<17),
165 }, {
166 .name = "lcd",
167 .id = -1,
168 .parent = &clk_h166,
169 .enable = s5pv210_clk_ip1_ctrl,
170 .ctrlbit = (1<<0),
171 }, {
172 .name = "cfcon",
173 .id = 0,
174 .parent = &clk_h133,
175 .enable = s5pv210_clk_ip1_ctrl,
176 .ctrlbit = (1<<25),
177 }, {
178 .name = "hsmmc",
179 .id = 0,
180 .parent = &clk_h133,
181 .enable = s5pv210_clk_ip2_ctrl,
182 .ctrlbit = (1<<16),
183 }, {
184 .name = "hsmmc",
185 .id = 1,
186 .parent = &clk_h133,
187 .enable = s5pv210_clk_ip2_ctrl,
188 .ctrlbit = (1<<17),
189 }, {
190 .name = "hsmmc",
191 .id = 2,
192 .parent = &clk_h133,
193 .enable = s5pv210_clk_ip2_ctrl,
194 .ctrlbit = (1<<18),
195 }, {
196 .name = "hsmmc",
197 .id = 3,
198 .parent = &clk_h133,
199 .enable = s5pv210_clk_ip2_ctrl,
200 .ctrlbit = (1<<19),
201 }, {
202 .name = "systimer",
203 .id = -1,
204 .parent = &clk_p66,
205 .enable = s5pv210_clk_ip3_ctrl,
206 .ctrlbit = (1<<16),
207 }, {
208 .name = "watchdog",
209 .id = -1,
210 .parent = &clk_p66,
211 .enable = s5pv210_clk_ip3_ctrl,
212 .ctrlbit = (1<<22),
213 }, {
214 .name = "rtc",
215 .id = -1,
216 .parent = &clk_p66,
217 .enable = s5pv210_clk_ip3_ctrl,
218 .ctrlbit = (1<<15),
219 }, {
220 .name = "i2c",
221 .id = 0,
222 .parent = &clk_p66,
223 .enable = s5pv210_clk_ip3_ctrl,
224 .ctrlbit = (1<<7),
225 }, {
226 .name = "i2c",
227 .id = 1,
228 .parent = &clk_p66,
229 .enable = s5pv210_clk_ip3_ctrl,
230 .ctrlbit = (1<<8),
231 }, {
232 .name = "i2c",
233 .id = 2,
234 .parent = &clk_p66,
235 .enable = s5pv210_clk_ip3_ctrl,
236 .ctrlbit = (1<<9),
237 }, {
238 .name = "spi",
239 .id = 0,
240 .parent = &clk_p66,
241 .enable = s5pv210_clk_ip3_ctrl,
242 .ctrlbit = (1<<12),
243 }, {
244 .name = "spi",
245 .id = 1,
246 .parent = &clk_p66,
247 .enable = s5pv210_clk_ip3_ctrl,
248 .ctrlbit = (1<<13),
249 }, {
250 .name = "spi",
251 .id = 2,
252 .parent = &clk_p66,
253 .enable = s5pv210_clk_ip3_ctrl,
254 .ctrlbit = (1<<14),
255 }, {
256 .name = "timers",
257 .id = -1,
258 .parent = &clk_p66,
259 .enable = s5pv210_clk_ip3_ctrl,
260 .ctrlbit = (1<<23),
261 }, {
262 .name = "adc",
263 .id = -1,
264 .parent = &clk_p66,
265 .enable = s5pv210_clk_ip3_ctrl,
266 .ctrlbit = (1<<24),
267 }, {
268 .name = "keypad",
269 .id = -1,
270 .parent = &clk_p66,
271 .enable = s5pv210_clk_ip3_ctrl,
272 .ctrlbit = (1<<21),
273 }, {
274 .name = "i2s_v50",
275 .id = 0,
276 .parent = &clk_p,
277 .enable = s5pv210_clk_ip3_ctrl,
278 .ctrlbit = (1<<4),
279 }, {
280 .name = "i2s_v32",
281 .id = 0,
282 .parent = &clk_p,
283 .enable = s5pv210_clk_ip3_ctrl,
284 .ctrlbit = (1<<4),
285 }, {
286 .name = "i2s_v32",
287 .id = 1,
288 .parent = &clk_p,
289 .enable = s5pv210_clk_ip3_ctrl,
290 .ctrlbit = (1<<4),
291 }
292};
293
294static struct clk init_clocks[] = {
295 {
296 .name = "uart",
297 .id = 0,
298 .parent = &clk_p66,
299 .enable = s5pv210_clk_ip3_ctrl,
300 .ctrlbit = (1<<7),
301 }, {
302 .name = "uart",
303 .id = 1,
304 .parent = &clk_p66,
305 .enable = s5pv210_clk_ip3_ctrl,
306 .ctrlbit = (1<<8),
307 }, {
308 .name = "uart",
309 .id = 2,
310 .parent = &clk_p66,
311 .enable = s5pv210_clk_ip3_ctrl,
312 .ctrlbit = (1<<9),
313 }, {
314 .name = "uart",
315 .id = 3,
316 .parent = &clk_p66,
317 .enable = s5pv210_clk_ip3_ctrl,
318 .ctrlbit = (1<<10),
319 },
320};
321
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900322static struct clk *clkset_uart_list[] = {
323 [6] = &clk_mout_mpll.clk,
324 [7] = &clk_mout_epll.clk,
325};
326
327static struct clksrc_sources clkset_uart = {
328 .sources = clkset_uart_list,
329 .nr_sources = ARRAY_SIZE(clkset_uart_list),
330};
331
332static struct clksrc_clk clksrcs[] = {
333 {
334 .clk = {
335 .name = "uclk1",
336 .id = -1,
337 .ctrlbit = (1<<17),
338 .enable = s5pv210_clk_ip3_ctrl,
339 },
340 .sources = &clkset_uart,
341 .reg_src = { .reg = S5P_CLK_SRC4, .shift = 16, .size = 4 },
342 .reg_div = { .reg = S5P_CLK_DIV4, .shift = 16, .size = 4 },
343 }
344};
345
346/* Clock initialisation code */
Thomas Abrahameb1ef1e2010-05-17 09:38:12 +0900347static struct clksrc_clk *sysclks[] = {
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900348 &clk_mout_apll,
349 &clk_mout_epll,
350 &clk_mout_mpll,
Thomas Abraham374e0bf2010-05-17 09:38:31 +0900351 &clk_armclk,
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900352};
353
354#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
355
356void __init_or_cpufreq s5pv210_setup_clocks(void)
357{
358 struct clk *xtal_clk;
359 unsigned long xtal;
360 unsigned long armclk;
361 unsigned long hclk200;
362 unsigned long hclk166;
363 unsigned long hclk133;
364 unsigned long pclk100;
365 unsigned long pclk83;
366 unsigned long pclk66;
367 unsigned long apll;
368 unsigned long mpll;
369 unsigned long epll;
370 unsigned int ptr;
371 u32 clkdiv0, clkdiv1;
372
373 printk(KERN_DEBUG "%s: registering clocks\n", __func__);
374
375 clkdiv0 = __raw_readl(S5P_CLK_DIV0);
376 clkdiv1 = __raw_readl(S5P_CLK_DIV1);
377
378 printk(KERN_DEBUG "%s: clkdiv0 = %08x, clkdiv1 = %08x\n",
379 __func__, clkdiv0, clkdiv1);
380
381 xtal_clk = clk_get(NULL, "xtal");
382 BUG_ON(IS_ERR(xtal_clk));
383
384 xtal = clk_get_rate(xtal_clk);
385 clk_put(xtal_clk);
386
387 printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
388
389 apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON), pll_4508);
390 mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON), pll_4502);
391 epll = s5p_get_pll45xx(xtal, __raw_readl(S5P_EPLL_CON), pll_4500);
392
Thomas Abrahamc62ec6a2010-05-17 09:38:28 +0900393 clk_fout_apll.rate = apll;
394 clk_fout_mpll.rate = mpll;
395 clk_fout_epll.rate = epll;
396
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900397 printk(KERN_INFO "S5PV210: PLL settings, A=%ld, M=%ld, E=%ld",
398 apll, mpll, epll);
399
Thomas Abraham374e0bf2010-05-17 09:38:31 +0900400 armclk = clk_get_rate(&clk_armclk.clk);
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900401 if (__raw_readl(S5P_CLK_SRC0) & S5P_CLKSRC0_MUX200_MASK)
402 hclk200 = mpll / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK200);
403 else
404 hclk200 = armclk / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK200);
405
406 if (__raw_readl(S5P_CLK_SRC0) & S5P_CLKSRC0_MUX166_MASK) {
407 hclk166 = apll / GET_DIV(clkdiv0, S5P_CLKDIV0_A2M);
408 hclk166 = hclk166 / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK166);
409 } else
410 hclk166 = mpll / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK166);
411
412 if (__raw_readl(S5P_CLK_SRC0) & S5P_CLKSRC0_MUX133_MASK) {
413 hclk133 = apll / GET_DIV(clkdiv0, S5P_CLKDIV0_A2M);
414 hclk133 = hclk133 / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK133);
415 } else
416 hclk133 = mpll / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK133);
417
418 pclk100 = hclk200 / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK100);
419 pclk83 = hclk166 / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK83);
420 pclk66 = hclk133 / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK66);
421
422 printk(KERN_INFO "S5PV210: ARMCLK=%ld, HCLKM=%ld, HCLKD=%ld, \
423 HCLKP=%ld, PCLKM=%ld, PCLKD=%ld, PCLKP=%ld\n",
424 armclk, hclk200, hclk166, hclk133, pclk100, pclk83, pclk66);
425
Kukjin Kim0c1945d2010-02-24 16:40:36 +0900426 clk_f.rate = armclk;
427 clk_h.rate = hclk133;
428 clk_p.rate = pclk66;
429 clk_p66.rate = pclk66;
430 clk_p83.rate = pclk83;
431 clk_h133.rate = hclk133;
432 clk_h166.rate = hclk166;
433 clk_h200.rate = hclk200;
434
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}