ARM: S3C64XX: Merge s3c6400-init.c into cpu.c
[deliverable/linux.git] / arch / arm / plat-s3c64xx / s3c6400-clock.c
CommitLineData
cf18acf0
BD
1/* linux/arch/arm/plat-s3c64xx/s3c6400-clock.c
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C6400 based common clock support
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/list.h>
19#include <linux/errno.h>
20#include <linux/err.h>
21#include <linux/clk.h>
22#include <linux/sysdev.h>
23#include <linux/io.h>
24
25#include <mach/hardware.h>
26#include <mach/map.h>
27
28#include <plat/cpu-freq.h>
29
3501c9ae 30#include <mach/regs-clock.h>
cf18acf0 31#include <plat/clock.h>
399cae74 32#include <plat/clock-clksrc.h>
cf18acf0
BD
33#include <plat/cpu.h>
34#include <plat/pll.h>
35
36/* fin_apll, fin_mpll and fin_epll are all the same clock, which we call
37 * ext_xtal_mux for want of an actual name from the manual.
38*/
39
3782d360 40static struct clk clk_ext_xtal_mux = {
cf18acf0
BD
41 .name = "ext_xtal",
42 .id = -1,
43};
44
45#define clk_fin_apll clk_ext_xtal_mux
46#define clk_fin_mpll clk_ext_xtal_mux
47#define clk_fin_epll clk_ext_xtal_mux
48
49#define clk_fout_mpll clk_mpll
87d26d2d 50#define clk_fout_epll clk_epll
cf18acf0 51
3782d360 52static struct clk clk_fout_apll = {
cf18acf0
BD
53 .name = "fout_apll",
54 .id = -1,
55};
56
57static struct clk *clk_src_apll_list[] = {
58 [0] = &clk_fin_apll,
59 [1] = &clk_fout_apll,
60};
61
399cae74 62static struct clksrc_sources clk_src_apll = {
cf18acf0
BD
63 .sources = clk_src_apll_list,
64 .nr_sources = ARRAY_SIZE(clk_src_apll_list),
65};
66
3782d360 67static struct clksrc_clk clk_mout_apll = {
cf18acf0
BD
68 .clk = {
69 .name = "mout_apll",
70 .id = -1,
71 },
f3e0b724 72 .reg_src = { .reg = S3C_CLK_SRC, .shift = 0, .size = 1 },
cf18acf0
BD
73 .sources = &clk_src_apll,
74};
75
cf18acf0
BD
76static struct clk *clk_src_epll_list[] = {
77 [0] = &clk_fin_epll,
78 [1] = &clk_fout_epll,
79};
80
399cae74 81static struct clksrc_sources clk_src_epll = {
cf18acf0
BD
82 .sources = clk_src_epll_list,
83 .nr_sources = ARRAY_SIZE(clk_src_epll_list),
84};
85
3782d360 86static struct clksrc_clk clk_mout_epll = {
cf18acf0
BD
87 .clk = {
88 .name = "mout_epll",
89 .id = -1,
90 },
f3e0b724 91 .reg_src = { .reg = S3C_CLK_SRC, .shift = 2, .size = 1 },
cf18acf0
BD
92 .sources = &clk_src_epll,
93};
94
95static struct clk *clk_src_mpll_list[] = {
96 [0] = &clk_fin_mpll,
97 [1] = &clk_fout_mpll,
98};
99
399cae74 100static struct clksrc_sources clk_src_mpll = {
cf18acf0
BD
101 .sources = clk_src_mpll_list,
102 .nr_sources = ARRAY_SIZE(clk_src_mpll_list),
103};
104
3782d360 105static struct clksrc_clk clk_mout_mpll = {
cf18acf0
BD
106 .clk = {
107 .name = "mout_mpll",
108 .id = -1,
109 },
f3e0b724 110 .reg_src = { .reg = S3C_CLK_SRC, .shift = 1, .size = 1 },
cf18acf0
BD
111 .sources = &clk_src_mpll,
112};
113
496a3f09
BD
114static unsigned int armclk_mask;
115
116static unsigned long s3c64xx_clk_arm_get_rate(struct clk *clk)
117{
118 unsigned long rate = clk_get_rate(clk->parent);
119 u32 clkdiv;
120
121 /* divisor mask starts at bit0, so no need to shift */
122 clkdiv = __raw_readl(S3C_CLK_DIV0) & armclk_mask;
123
124 return rate / (clkdiv + 1);
125}
126
127static unsigned long s3c64xx_clk_arm_round_rate(struct clk *clk,
128 unsigned long rate)
129{
130 unsigned long parent = clk_get_rate(clk->parent);
131 u32 div;
132
133 if (parent < rate)
1d91e1a2 134 return parent;
496a3f09
BD
135
136 div = (parent / rate) - 1;
137 if (div > armclk_mask)
138 div = armclk_mask;
139
140 return parent / (div + 1);
141}
142
143static int s3c64xx_clk_arm_set_rate(struct clk *clk, unsigned long rate)
144{
145 unsigned long parent = clk_get_rate(clk->parent);
146 u32 div;
147 u32 val;
148
149 if (rate < parent / (armclk_mask + 1))
150 return -EINVAL;
151
152 rate = clk_round_rate(clk, rate);
153 div = clk_get_rate(clk->parent) / rate;
154
155 val = __raw_readl(S3C_CLK_DIV0);
9b71de49 156 val &= ~armclk_mask;
496a3f09
BD
157 val |= (div - 1);
158 __raw_writel(val, S3C_CLK_DIV0);
159
160 return 0;
161
162}
163
164static struct clk clk_arm = {
165 .name = "armclk",
166 .id = -1,
167 .parent = &clk_mout_apll.clk,
b3bf41be
BD
168 .ops = &(struct clk_ops) {
169 .get_rate = s3c64xx_clk_arm_get_rate,
170 .set_rate = s3c64xx_clk_arm_set_rate,
171 .round_rate = s3c64xx_clk_arm_round_rate,
172 },
496a3f09
BD
173};
174
cf18acf0
BD
175static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk)
176{
177 unsigned long rate = clk_get_rate(clk->parent);
178
39669f59 179 printk(KERN_DEBUG "%s: parent is %ld\n", __func__, rate);
cf18acf0
BD
180
181 if (__raw_readl(S3C_CLK_DIV0) & S3C6400_CLKDIV0_MPLL_MASK)
182 rate /= 2;
183
184 return rate;
185}
186
b3bf41be
BD
187static struct clk_ops clk_dout_ops = {
188 .get_rate = s3c64xx_clk_doutmpll_get_rate,
189};
190
3782d360 191static struct clk clk_dout_mpll = {
cf18acf0
BD
192 .name = "dout_mpll",
193 .id = -1,
194 .parent = &clk_mout_mpll.clk,
b3bf41be 195 .ops = &clk_dout_ops,
cf18acf0
BD
196};
197
198static struct clk *clkset_spi_mmc_list[] = {
199 &clk_mout_epll.clk,
200 &clk_dout_mpll,
201 &clk_fin_epll,
202 &clk_27m,
203};
204
399cae74 205static struct clksrc_sources clkset_spi_mmc = {
cf18acf0
BD
206 .sources = clkset_spi_mmc_list,
207 .nr_sources = ARRAY_SIZE(clkset_spi_mmc_list),
208};
209
210static struct clk *clkset_irda_list[] = {
211 &clk_mout_epll.clk,
212 &clk_dout_mpll,
213 NULL,
214 &clk_27m,
215};
216
399cae74 217static struct clksrc_sources clkset_irda = {
cf18acf0
BD
218 .sources = clkset_irda_list,
219 .nr_sources = ARRAY_SIZE(clkset_irda_list),
220};
221
222static struct clk *clkset_uart_list[] = {
223 &clk_mout_epll.clk,
224 &clk_dout_mpll,
225 NULL,
226 NULL
227};
228
399cae74 229static struct clksrc_sources clkset_uart = {
cf18acf0
BD
230 .sources = clkset_uart_list,
231 .nr_sources = ARRAY_SIZE(clkset_uart_list),
232};
233
234static struct clk *clkset_uhost_list[] = {
41ba41d7 235 &clk_48m,
cf18acf0
BD
236 &clk_mout_epll.clk,
237 &clk_dout_mpll,
238 &clk_fin_epll,
cf18acf0
BD
239};
240
399cae74 241static struct clksrc_sources clkset_uhost = {
cf18acf0
BD
242 .sources = clkset_uhost_list,
243 .nr_sources = ARRAY_SIZE(clkset_uhost_list),
244};
245
cf18acf0
BD
246/* The peripheral clocks are all controlled via clocksource followed
247 * by an optional divider and gate stage. We currently roll this into
248 * one clock which hides the intermediate clock from the mux.
249 *
250 * Note, the JPEG clock can only be an even divider...
251 *
252 * The scaler and LCD clocks depend on the S3C64XX version, and also
253 * have a common parent divisor so are not included here.
254 */
255
8360493c 256/* clocks that feed other parts of the clock source tree */
cf18acf0
BD
257
258static struct clk clk_iis_cd0 = {
259 .name = "iis_cdclk0",
260 .id = -1,
261};
262
263static struct clk clk_iis_cd1 = {
264 .name = "iis_cdclk1",
265 .id = -1,
266};
267
268static struct clk clk_pcm_cd = {
269 .name = "pcm_cdclk",
270 .id = -1,
271};
272
273static struct clk *clkset_audio0_list[] = {
274 [0] = &clk_mout_epll.clk,
275 [1] = &clk_dout_mpll,
276 [2] = &clk_fin_epll,
277 [3] = &clk_iis_cd0,
278 [4] = &clk_pcm_cd,
279};
280
399cae74 281static struct clksrc_sources clkset_audio0 = {
cf18acf0
BD
282 .sources = clkset_audio0_list,
283 .nr_sources = ARRAY_SIZE(clkset_audio0_list),
284};
285
cf18acf0
BD
286static struct clk *clkset_audio1_list[] = {
287 [0] = &clk_mout_epll.clk,
288 [1] = &clk_dout_mpll,
289 [2] = &clk_fin_epll,
290 [3] = &clk_iis_cd1,
291 [4] = &clk_pcm_cd,
292};
293
399cae74 294static struct clksrc_sources clkset_audio1 = {
cf18acf0
BD
295 .sources = clkset_audio1_list,
296 .nr_sources = ARRAY_SIZE(clkset_audio1_list),
297};
298
e2c977dc
WA
299static struct clk *clkset_camif_list[] = {
300 &clk_h2,
301};
302
399cae74 303static struct clksrc_sources clkset_camif = {
e2c977dc
WA
304 .sources = clkset_camif_list,
305 .nr_sources = ARRAY_SIZE(clkset_camif_list),
306};
307
8360493c
BD
308static struct clksrc_clk clksrcs[] = {
309 {
310 .clk = {
311 .name = "mmc_bus",
312 .id = 0,
313 .ctrlbit = S3C_CLKCON_SCLK_MMC0,
314 .enable = s3c64xx_sclk_ctrl,
315 },
f3e0b724
BD
316 .reg_src = { .reg = S3C_CLK_SRC, .shift = 18, .size = 2 },
317 .reg_div = { .reg = S3C_CLK_DIV1, .shift = 0, .size = 4 },
8360493c 318 .sources = &clkset_spi_mmc,
8360493c
BD
319 }, {
320 .clk = {
321 .name = "mmc_bus",
322 .id = 1,
323 .ctrlbit = S3C_CLKCON_SCLK_MMC1,
324 .enable = s3c64xx_sclk_ctrl,
325 },
f3e0b724
BD
326 .reg_src = { .reg = S3C_CLK_SRC, .shift = 20, .size = 2 },
327 .reg_div = { .reg = S3C_CLK_DIV1, .shift = 4, .size = 4 },
8360493c 328 .sources = &clkset_spi_mmc,
8360493c
BD
329 }, {
330 .clk = {
331 .name = "mmc_bus",
332 .id = 2,
333 .ctrlbit = S3C_CLKCON_SCLK_MMC2,
334 .enable = s3c64xx_sclk_ctrl,
335 },
f3e0b724
BD
336 .reg_src = { .reg = S3C_CLK_SRC, .shift = 22, .size = 2 },
337 .reg_div = { .reg = S3C_CLK_DIV1, .shift = 8, .size = 4 },
8360493c 338 .sources = &clkset_spi_mmc,
8360493c
BD
339 }, {
340 .clk = {
341 .name = "usb-bus-host",
342 .id = -1,
343 .ctrlbit = S3C_CLKCON_SCLK_UHOST,
344 .enable = s3c64xx_sclk_ctrl,
345 },
f3e0b724
BD
346 .reg_src = { .reg = S3C_CLK_SRC, .shift = 5, .size = 2 },
347 .reg_div = { .reg = S3C_CLK_DIV1, .shift = 20, .size = 4 },
8360493c 348 .sources = &clkset_uhost,
8360493c
BD
349 }, {
350 .clk = {
351 .name = "uclk1",
352 .id = -1,
353 .ctrlbit = S3C_CLKCON_SCLK_UART,
354 .enable = s3c64xx_sclk_ctrl,
355 },
f3e0b724
BD
356 .reg_src = { .reg = S3C_CLK_SRC, .shift = 13, .size = 1 },
357 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 16, .size = 4 },
8360493c 358 .sources = &clkset_uart,
8360493c
BD
359 }, {
360/* Where does UCLK0 come from? */
361 .clk = {
362 .name = "spi-bus",
363 .id = 0,
364 .ctrlbit = S3C_CLKCON_SCLK_SPI0,
365 .enable = s3c64xx_sclk_ctrl,
366 },
f3e0b724
BD
367 .reg_src = { .reg = S3C_CLK_SRC, .shift = 14, .size = 2 },
368 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 0, .size = 4 },
8360493c 369 .sources = &clkset_spi_mmc,
8360493c
BD
370 }, {
371 .clk = {
372 .name = "spi-bus",
373 .id = 1,
374 .ctrlbit = S3C_CLKCON_SCLK_SPI1,
375 .enable = s3c64xx_sclk_ctrl,
376 },
f3e0b724
BD
377 .reg_src = { .reg = S3C_CLK_SRC, .shift = 16, .size = 2 },
378 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 4, .size = 4 },
8360493c 379 .sources = &clkset_spi_mmc,
8360493c
BD
380 }, {
381 .clk = {
382 .name = "audio-bus",
383 .id = 0,
384 .ctrlbit = S3C_CLKCON_SCLK_AUDIO0,
385 .enable = s3c64xx_sclk_ctrl,
386 },
f3e0b724
BD
387 .reg_src = { .reg = S3C_CLK_SRC, .shift = 7, .size = 3 },
388 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 8, .size = 4 },
8360493c 389 .sources = &clkset_audio0,
8360493c
BD
390 }, {
391 .clk = {
392 .name = "audio-bus",
393 .id = 1,
394 .ctrlbit = S3C_CLKCON_SCLK_AUDIO1,
395 .enable = s3c64xx_sclk_ctrl,
396 },
f3e0b724
BD
397 .reg_src = { .reg = S3C_CLK_SRC, .shift = 10, .size = 3 },
398 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 12, .size = 4 },
8360493c 399 .sources = &clkset_audio1,
8360493c
BD
400 }, {
401 .clk = {
402 .name = "irda-bus",
403 .id = 0,
404 .ctrlbit = S3C_CLKCON_SCLK_IRDA,
405 .enable = s3c64xx_sclk_ctrl,
406 },
f3e0b724
BD
407 .reg_src = { .reg = S3C_CLK_SRC, .shift = 24, .size = 2 },
408 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 20, .size = 4 },
8360493c 409 .sources = &clkset_irda,
8360493c
BD
410 }, {
411 .clk = {
412 .name = "camera",
413 .id = -1,
414 .ctrlbit = S3C_CLKCON_SCLK_CAM,
415 .enable = s3c64xx_sclk_ctrl,
416 },
f3e0b724
BD
417 .reg_div = { .reg = S3C_CLK_DIV0, .shift = 20, .size = 4 },
418 .reg_src = { .reg = NULL, .shift = 0, .size = 0 },
8360493c 419 .sources = &clkset_camif,
e2c977dc 420 },
e2c977dc
WA
421};
422
cf18acf0
BD
423/* Clock initialisation code */
424
425static struct clksrc_clk *init_parents[] = {
426 &clk_mout_apll,
427 &clk_mout_epll,
428 &clk_mout_mpll,
cf18acf0
BD
429};
430
cf18acf0
BD
431#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
432
433void __init_or_cpufreq s3c6400_setup_clocks(void)
434{
435 struct clk *xtal_clk;
436 unsigned long xtal;
437 unsigned long fclk;
438 unsigned long hclk;
439 unsigned long hclk2;
440 unsigned long pclk;
441 unsigned long epll;
442 unsigned long apll;
443 unsigned long mpll;
444 unsigned int ptr;
445 u32 clkdiv0;
446
39669f59 447 printk(KERN_DEBUG "%s: registering clocks\n", __func__);
cf18acf0
BD
448
449 clkdiv0 = __raw_readl(S3C_CLK_DIV0);
39669f59 450 printk(KERN_DEBUG "%s: clkdiv0 = %08x\n", __func__, clkdiv0);
cf18acf0
BD
451
452 xtal_clk = clk_get(NULL, "xtal");
453 BUG_ON(IS_ERR(xtal_clk));
454
455 xtal = clk_get_rate(xtal_clk);
456 clk_put(xtal_clk);
457
39669f59 458 printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
cf18acf0 459
e179ac0f
MB
460 /* For now assume the mux always selects the crystal */
461 clk_ext_xtal_mux.parent = xtal_clk;
462
cf18acf0
BD
463 epll = s3c6400_get_epll(xtal);
464 mpll = s3c6400_get_pll(xtal, __raw_readl(S3C_MPLL_CON));
465 apll = s3c6400_get_pll(xtal, __raw_readl(S3C_APLL_CON));
466
467 fclk = mpll;
468
469 printk(KERN_INFO "S3C64XX: PLL settings, A=%ld, M=%ld, E=%ld\n",
470 apll, mpll, epll);
471
472 hclk2 = mpll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2);
473 hclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK);
474 pclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_PCLK);
475
476 printk(KERN_INFO "S3C64XX: HCLK2=%ld, HCLK=%ld, PCLK=%ld\n",
477 hclk2, hclk, pclk);
478
479 clk_fout_mpll.rate = mpll;
480 clk_fout_epll.rate = epll;
481 clk_fout_apll.rate = apll;
482
a03f7daf 483 clk_h2.rate = hclk2;
cf18acf0
BD
484 clk_h.rate = hclk;
485 clk_p.rate = pclk;
486 clk_f.rate = fclk;
487
488 for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++)
682e2b7d 489 s3c_set_clksrc(init_parents[ptr], true);
8360493c
BD
490
491 for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++)
682e2b7d 492 s3c_set_clksrc(&clksrcs[ptr], true);
cf18acf0
BD
493}
494
495static struct clk *clks[] __initdata = {
496 &clk_ext_xtal_mux,
497 &clk_iis_cd0,
498 &clk_iis_cd1,
499 &clk_pcm_cd,
500 &clk_mout_epll.clk,
501 &clk_mout_mpll.clk,
502 &clk_dout_mpll,
496a3f09 503 &clk_arm,
cf18acf0
BD
504};
505
496a3f09
BD
506/**
507 * s3c6400_register_clocks - register clocks for s3c6400 and above
508 * @armclk_divlimit: Divisor mask for ARMCLK
509 *
510 * Register the clocks for the S3C6400 and above SoC range, such
511 * as ARMCLK and the clocks which have divider chains attached.
512 *
513 * This call does not setup the clocks, which is left to the
514 * s3c6400_setup_clocks() call which may be needed by the cpufreq
515 * or resume code to re-set the clocks if the bootloader has changed
516 * them.
517 */
518void __init s3c6400_register_clocks(unsigned armclk_divlimit)
cf18acf0
BD
519{
520 struct clk *clkp;
521 int ret;
522 int ptr;
523
496a3f09
BD
524 armclk_mask = armclk_divlimit;
525
cf18acf0
BD
526 for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
527 clkp = clks[ptr];
528 ret = s3c24xx_register_clock(clkp);
529 if (ret < 0) {
530 printk(KERN_ERR "Failed to register clock %s (%d)\n",
531 clkp->name, ret);
532 }
533 }
8360493c 534
399cae74 535 s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
cf18acf0 536}
This page took 0.112247 seconds and 5 git commands to generate.