• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* linux/arch/arm/mach-s3c2412/clock.c
2  *
3  * Copyright (c) 2006 Simtec Electronics
4  *	Ben Dooks <ben@simtec.co.uk>
5  *
6  * S3C2412,S3C2413 Clock control 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 as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22 
23 #include <linux/init.h>
24 #include <linux/module.h>
25 #include <linux/kernel.h>
26 #include <linux/list.h>
27 #include <linux/errno.h>
28 #include <linux/err.h>
29 #include <linux/sysdev.h>
30 #include <linux/clk.h>
31 #include <linux/mutex.h>
32 #include <linux/delay.h>
33 #include <linux/serial_core.h>
34 #include <linux/io.h>
35 
36 #include <asm/mach/map.h>
37 
38 #include <mach/hardware.h>
39 
40 #include <plat/regs-serial.h>
41 #include <mach/regs-clock.h>
42 #include <mach/regs-gpio.h>
43 
44 #include <plat/s3c2412.h>
45 #include <plat/clock.h>
46 #include <plat/cpu.h>
47 
48 /* We currently have to assume that the system is running
49  * from the XTPll input, and that all ***REFCLKs are being
50  * fed from it, as we cannot read the state of OM[4] from
51  * software.
52  *
53  * It would be possible for each board initialisation to
54  * set the correct muxing at initialisation
55 */
56 
s3c2412_clkcon_enable(struct clk * clk,int enable)57 static int s3c2412_clkcon_enable(struct clk *clk, int enable)
58 {
59 	unsigned int clocks = clk->ctrlbit;
60 	unsigned long clkcon;
61 
62 	clkcon = __raw_readl(S3C2410_CLKCON);
63 
64 	if (enable)
65 		clkcon |= clocks;
66 	else
67 		clkcon &= ~clocks;
68 
69 	__raw_writel(clkcon, S3C2410_CLKCON);
70 
71 	return 0;
72 }
73 
s3c2412_upll_enable(struct clk * clk,int enable)74 static int s3c2412_upll_enable(struct clk *clk, int enable)
75 {
76 	unsigned long upllcon = __raw_readl(S3C2410_UPLLCON);
77 	unsigned long orig = upllcon;
78 
79 	if (!enable)
80 		upllcon |= S3C2412_PLLCON_OFF;
81 	else
82 		upllcon &= ~S3C2412_PLLCON_OFF;
83 
84 	__raw_writel(upllcon, S3C2410_UPLLCON);
85 
86 	/* allow ~150uS for the PLL to settle and lock */
87 
88 	if (enable && (orig & S3C2412_PLLCON_OFF))
89 		udelay(150);
90 
91 	return 0;
92 }
93 
94 /* clock selections */
95 
96 static struct clk clk_erefclk = {
97 	.name		= "erefclk",
98 	.id		= -1,
99 };
100 
101 static struct clk clk_urefclk = {
102 	.name		= "urefclk",
103 	.id		= -1,
104 };
105 
s3c2412_setparent_usysclk(struct clk * clk,struct clk * parent)106 static int s3c2412_setparent_usysclk(struct clk *clk, struct clk *parent)
107 {
108 	unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
109 
110 	if (parent == &clk_urefclk)
111 		clksrc &= ~S3C2412_CLKSRC_USYSCLK_UPLL;
112 	else if (parent == &clk_upll)
113 		clksrc |= S3C2412_CLKSRC_USYSCLK_UPLL;
114 	else
115 		return -EINVAL;
116 
117 	clk->parent = parent;
118 
119 	__raw_writel(clksrc, S3C2412_CLKSRC);
120 	return 0;
121 }
122 
123 static struct clk clk_usysclk = {
124 	.name		= "usysclk",
125 	.id		= -1,
126 	.parent		= &clk_xtal,
127 	.set_parent	= s3c2412_setparent_usysclk,
128 };
129 
130 static struct clk clk_mrefclk = {
131 	.name		= "mrefclk",
132 	.parent		= &clk_xtal,
133 	.id		= -1,
134 };
135 
136 static struct clk clk_mdivclk = {
137 	.name		= "mdivclk",
138 	.parent		= &clk_xtal,
139 	.id		= -1,
140 };
141 
s3c2412_setparent_usbsrc(struct clk * clk,struct clk * parent)142 static int s3c2412_setparent_usbsrc(struct clk *clk, struct clk *parent)
143 {
144 	unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
145 
146 	if (parent == &clk_usysclk)
147 		clksrc &= ~S3C2412_CLKSRC_USBCLK_HCLK;
148 	else if (parent == &clk_h)
149 		clksrc |= S3C2412_CLKSRC_USBCLK_HCLK;
150 	else
151 		return -EINVAL;
152 
153 	clk->parent = parent;
154 
155 	__raw_writel(clksrc, S3C2412_CLKSRC);
156 	return 0;
157 }
158 
s3c2412_roundrate_usbsrc(struct clk * clk,unsigned long rate)159 static unsigned long s3c2412_roundrate_usbsrc(struct clk *clk,
160 					      unsigned long rate)
161 {
162 	unsigned long parent_rate = clk_get_rate(clk->parent);
163 	int div;
164 
165 	if (rate > parent_rate)
166 		return parent_rate;
167 
168 	div = parent_rate / rate;
169 	if (div > 2)
170 		div = 2;
171 
172 	return parent_rate / div;
173 }
174 
s3c2412_getrate_usbsrc(struct clk * clk)175 static unsigned long s3c2412_getrate_usbsrc(struct clk *clk)
176 {
177 	unsigned long parent_rate = clk_get_rate(clk->parent);
178 	unsigned long div = __raw_readl(S3C2410_CLKDIVN);
179 
180 	return parent_rate / ((div & S3C2412_CLKDIVN_USB48DIV) ? 2 : 1);
181 }
182 
s3c2412_setrate_usbsrc(struct clk * clk,unsigned long rate)183 static int s3c2412_setrate_usbsrc(struct clk *clk, unsigned long rate)
184 {
185 	unsigned long parent_rate = clk_get_rate(clk->parent);
186 	unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN);
187 
188 	rate = s3c2412_roundrate_usbsrc(clk, rate);
189 
190 	if ((parent_rate / rate) == 2)
191 		clkdivn |= S3C2412_CLKDIVN_USB48DIV;
192 	else
193 		clkdivn &= ~S3C2412_CLKDIVN_USB48DIV;
194 
195 	__raw_writel(clkdivn, S3C2410_CLKDIVN);
196 	return 0;
197 }
198 
199 static struct clk clk_usbsrc = {
200 	.name		= "usbsrc",
201 	.id		= -1,
202 	.get_rate	= s3c2412_getrate_usbsrc,
203 	.set_rate	= s3c2412_setrate_usbsrc,
204 	.round_rate	= s3c2412_roundrate_usbsrc,
205 	.set_parent	= s3c2412_setparent_usbsrc,
206 };
207 
s3c2412_setparent_msysclk(struct clk * clk,struct clk * parent)208 static int s3c2412_setparent_msysclk(struct clk *clk, struct clk *parent)
209 {
210 	unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
211 
212 	if (parent == &clk_mdivclk)
213 		clksrc &= ~S3C2412_CLKSRC_MSYSCLK_MPLL;
214 	else if (parent == &clk_mpll)
215 		clksrc |= S3C2412_CLKSRC_MSYSCLK_MPLL;
216 	else
217 		return -EINVAL;
218 
219 	clk->parent = parent;
220 
221 	__raw_writel(clksrc, S3C2412_CLKSRC);
222 	return 0;
223 }
224 
225 static struct clk clk_msysclk = {
226 	.name		= "msysclk",
227 	.id		= -1,
228 	.set_parent	= s3c2412_setparent_msysclk,
229 };
230 
s3c2412_setparent_armclk(struct clk * clk,struct clk * parent)231 static int s3c2412_setparent_armclk(struct clk *clk, struct clk *parent)
232 {
233 	unsigned long flags;
234 	unsigned long clkdiv;
235 	unsigned long dvs;
236 
237 	/* Note, we current equate fclk andf msysclk for S3C2412 */
238 
239 	if (parent == &clk_msysclk || parent == &clk_f)
240 		dvs = 0;
241 	else if (parent == &clk_h)
242 		dvs = S3C2412_CLKDIVN_DVSEN;
243 	else
244 		return -EINVAL;
245 
246 	clk->parent = parent;
247 
248 	/* update this under irq lockdown, clkdivn is not protected
249 	 * by the clock system. */
250 
251 	local_irq_save(flags);
252 
253 	clkdiv  = __raw_readl(S3C2410_CLKDIVN);
254 	clkdiv &= ~S3C2412_CLKDIVN_DVSEN;
255 	clkdiv |= dvs;
256 	__raw_writel(clkdiv, S3C2410_CLKDIVN);
257 
258 	local_irq_restore(flags);
259 
260 	return 0;
261 }
262 
263 static struct clk clk_armclk = {
264 	.name		= "armclk",
265 	.id		= -1,
266 	.parent		= &clk_msysclk,
267 	.set_parent	= s3c2412_setparent_armclk,
268 };
269 
270 /* these next clocks have an divider immediately after them,
271  * so we can register them with their divider and leave out the
272  * intermediate clock stage
273 */
s3c2412_roundrate_clksrc(struct clk * clk,unsigned long rate)274 static unsigned long s3c2412_roundrate_clksrc(struct clk *clk,
275 					      unsigned long rate)
276 {
277 	unsigned long parent_rate = clk_get_rate(clk->parent);
278 	int div;
279 
280 	if (rate > parent_rate)
281 		return parent_rate;
282 
283 	/* note, we remove the +/- 1 calculations as they cancel out */
284 
285 	div = (rate / parent_rate);
286 
287 	if (div < 1)
288 		div = 1;
289 	else if (div > 16)
290 		div = 16;
291 
292 	return parent_rate / div;
293 }
294 
s3c2412_setparent_uart(struct clk * clk,struct clk * parent)295 static int s3c2412_setparent_uart(struct clk *clk, struct clk *parent)
296 {
297 	unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
298 
299 	if (parent == &clk_erefclk)
300 		clksrc &= ~S3C2412_CLKSRC_UARTCLK_MPLL;
301 	else if (parent == &clk_mpll)
302 		clksrc |= S3C2412_CLKSRC_UARTCLK_MPLL;
303 	else
304 		return -EINVAL;
305 
306 	clk->parent = parent;
307 
308 	__raw_writel(clksrc, S3C2412_CLKSRC);
309 	return 0;
310 }
311 
s3c2412_getrate_uart(struct clk * clk)312 static unsigned long s3c2412_getrate_uart(struct clk *clk)
313 {
314 	unsigned long parent_rate = clk_get_rate(clk->parent);
315 	unsigned long div = __raw_readl(S3C2410_CLKDIVN);
316 
317 	div &= S3C2412_CLKDIVN_UARTDIV_MASK;
318 	div >>= S3C2412_CLKDIVN_UARTDIV_SHIFT;
319 
320 	return parent_rate / (div + 1);
321 }
322 
s3c2412_setrate_uart(struct clk * clk,unsigned long rate)323 static int s3c2412_setrate_uart(struct clk *clk, unsigned long rate)
324 {
325 	unsigned long parent_rate = clk_get_rate(clk->parent);
326 	unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN);
327 
328 	rate = s3c2412_roundrate_clksrc(clk, rate);
329 
330 	clkdivn &= ~S3C2412_CLKDIVN_UARTDIV_MASK;
331 	clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_UARTDIV_SHIFT;
332 
333 	__raw_writel(clkdivn, S3C2410_CLKDIVN);
334 	return 0;
335 }
336 
337 static struct clk clk_uart = {
338 	.name		= "uartclk",
339 	.id		= -1,
340 	.get_rate	= s3c2412_getrate_uart,
341 	.set_rate	= s3c2412_setrate_uart,
342 	.set_parent	= s3c2412_setparent_uart,
343 	.round_rate	= s3c2412_roundrate_clksrc,
344 };
345 
s3c2412_setparent_i2s(struct clk * clk,struct clk * parent)346 static int s3c2412_setparent_i2s(struct clk *clk, struct clk *parent)
347 {
348 	unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
349 
350 	if (parent == &clk_erefclk)
351 		clksrc &= ~S3C2412_CLKSRC_I2SCLK_MPLL;
352 	else if (parent == &clk_mpll)
353 		clksrc |= S3C2412_CLKSRC_I2SCLK_MPLL;
354 	else
355 		return -EINVAL;
356 
357 	clk->parent = parent;
358 
359 	__raw_writel(clksrc, S3C2412_CLKSRC);
360 	return 0;
361 }
362 
s3c2412_getrate_i2s(struct clk * clk)363 static unsigned long s3c2412_getrate_i2s(struct clk *clk)
364 {
365 	unsigned long parent_rate = clk_get_rate(clk->parent);
366 	unsigned long div = __raw_readl(S3C2410_CLKDIVN);
367 
368 	div &= S3C2412_CLKDIVN_I2SDIV_MASK;
369 	div >>= S3C2412_CLKDIVN_I2SDIV_SHIFT;
370 
371 	return parent_rate / (div + 1);
372 }
373 
s3c2412_setrate_i2s(struct clk * clk,unsigned long rate)374 static int s3c2412_setrate_i2s(struct clk *clk, unsigned long rate)
375 {
376 	unsigned long parent_rate = clk_get_rate(clk->parent);
377 	unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN);
378 
379 	rate = s3c2412_roundrate_clksrc(clk, rate);
380 
381 	clkdivn &= ~S3C2412_CLKDIVN_I2SDIV_MASK;
382 	clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_I2SDIV_SHIFT;
383 
384 	__raw_writel(clkdivn, S3C2410_CLKDIVN);
385 	return 0;
386 }
387 
388 static struct clk clk_i2s = {
389 	.name		= "i2sclk",
390 	.id		= -1,
391 	.get_rate	= s3c2412_getrate_i2s,
392 	.set_rate	= s3c2412_setrate_i2s,
393 	.set_parent	= s3c2412_setparent_i2s,
394 	.round_rate	= s3c2412_roundrate_clksrc,
395 };
396 
s3c2412_setparent_cam(struct clk * clk,struct clk * parent)397 static int s3c2412_setparent_cam(struct clk *clk, struct clk *parent)
398 {
399 	unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
400 
401 	if (parent == &clk_usysclk)
402 		clksrc &= ~S3C2412_CLKSRC_CAMCLK_HCLK;
403 	else if (parent == &clk_h)
404 		clksrc |= S3C2412_CLKSRC_CAMCLK_HCLK;
405 	else
406 		return -EINVAL;
407 
408 	clk->parent = parent;
409 
410 	__raw_writel(clksrc, S3C2412_CLKSRC);
411 	return 0;
412 }
s3c2412_getrate_cam(struct clk * clk)413 static unsigned long s3c2412_getrate_cam(struct clk *clk)
414 {
415 	unsigned long parent_rate = clk_get_rate(clk->parent);
416 	unsigned long div = __raw_readl(S3C2410_CLKDIVN);
417 
418 	div &= S3C2412_CLKDIVN_CAMDIV_MASK;
419 	div >>= S3C2412_CLKDIVN_CAMDIV_SHIFT;
420 
421 	return parent_rate / (div + 1);
422 }
423 
s3c2412_setrate_cam(struct clk * clk,unsigned long rate)424 static int s3c2412_setrate_cam(struct clk *clk, unsigned long rate)
425 {
426 	unsigned long parent_rate = clk_get_rate(clk->parent);
427 	unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN);
428 
429 	rate = s3c2412_roundrate_clksrc(clk, rate);
430 
431 	clkdivn &= ~S3C2412_CLKDIVN_CAMDIV_MASK;
432 	clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_CAMDIV_SHIFT;
433 
434 	__raw_writel(clkdivn, S3C2410_CLKDIVN);
435 	return 0;
436 }
437 
438 static struct clk clk_cam = {
439 	.name		= "camif-upll",	/* same as 2440 name */
440 	.id		= -1,
441 	.get_rate	= s3c2412_getrate_cam,
442 	.set_rate	= s3c2412_setrate_cam,
443 	.set_parent	= s3c2412_setparent_cam,
444 	.round_rate	= s3c2412_roundrate_clksrc,
445 };
446 
447 /* standard clock definitions */
448 
449 static struct clk init_clocks_disable[] = {
450 	{
451 		.name		= "nand",
452 		.id		= -1,
453 		.parent		= &clk_h,
454 		.enable		= s3c2412_clkcon_enable,
455 		.ctrlbit	= S3C2412_CLKCON_NAND,
456 	}, {
457 		.name		= "sdi",
458 		.id		= -1,
459 		.parent		= &clk_p,
460 		.enable		= s3c2412_clkcon_enable,
461 		.ctrlbit	= S3C2412_CLKCON_SDI,
462 	}, {
463 		.name		= "adc",
464 		.id		= -1,
465 		.parent		= &clk_p,
466 		.enable		= s3c2412_clkcon_enable,
467 		.ctrlbit	= S3C2412_CLKCON_ADC,
468 	}, {
469 		.name		= "i2c",
470 		.id		= -1,
471 		.parent		= &clk_p,
472 		.enable		= s3c2412_clkcon_enable,
473 		.ctrlbit	= S3C2412_CLKCON_IIC,
474 	}, {
475 		.name		= "iis",
476 		.id		= -1,
477 		.parent		= &clk_p,
478 		.enable		= s3c2412_clkcon_enable,
479 		.ctrlbit	= S3C2412_CLKCON_IIS,
480 	}, {
481 		.name		= "spi",
482 		.id		= -1,
483 		.parent		= &clk_p,
484 		.enable		= s3c2412_clkcon_enable,
485 		.ctrlbit	= S3C2412_CLKCON_SPI,
486 	}
487 };
488 
489 static struct clk init_clocks[] = {
490 	{
491 		.name		= "dma",
492 		.id		= 0,
493 		.parent		= &clk_h,
494 		.enable		= s3c2412_clkcon_enable,
495 		.ctrlbit	= S3C2412_CLKCON_DMA0,
496 	}, {
497 		.name		= "dma",
498 		.id		= 1,
499 		.parent		= &clk_h,
500 		.enable		= s3c2412_clkcon_enable,
501 		.ctrlbit	= S3C2412_CLKCON_DMA1,
502 	}, {
503 		.name		= "dma",
504 		.id		= 2,
505 		.parent		= &clk_h,
506 		.enable		= s3c2412_clkcon_enable,
507 		.ctrlbit	= S3C2412_CLKCON_DMA2,
508 	}, {
509 		.name		= "dma",
510 		.id		= 3,
511 		.parent		= &clk_h,
512 		.enable		= s3c2412_clkcon_enable,
513 		.ctrlbit	= S3C2412_CLKCON_DMA3,
514 	}, {
515 		.name		= "lcd",
516 		.id		= -1,
517 		.parent		= &clk_h,
518 		.enable		= s3c2412_clkcon_enable,
519 		.ctrlbit	= S3C2412_CLKCON_LCDC,
520 	}, {
521 		.name		= "gpio",
522 		.id		= -1,
523 		.parent		= &clk_p,
524 		.enable		= s3c2412_clkcon_enable,
525 		.ctrlbit	= S3C2412_CLKCON_GPIO,
526 	}, {
527 		.name		= "usb-host",
528 		.id		= -1,
529 		.parent		= &clk_h,
530 		.enable		= s3c2412_clkcon_enable,
531 		.ctrlbit	= S3C2412_CLKCON_USBH,
532 	}, {
533 		.name		= "usb-device",
534 		.id		= -1,
535 		.parent		= &clk_h,
536 		.enable		= s3c2412_clkcon_enable,
537 		.ctrlbit	= S3C2412_CLKCON_USBD,
538 	}, {
539 		.name		= "timers",
540 		.id		= -1,
541 		.parent		= &clk_p,
542 		.enable		= s3c2412_clkcon_enable,
543 		.ctrlbit	= S3C2412_CLKCON_PWMT,
544 	}, {
545 		.name		= "uart",
546 		.id		= 0,
547 		.parent		= &clk_p,
548 		.enable		= s3c2412_clkcon_enable,
549 		.ctrlbit	= S3C2412_CLKCON_UART0,
550 	}, {
551 		.name		= "uart",
552 		.id		= 1,
553 		.parent		= &clk_p,
554 		.enable		= s3c2412_clkcon_enable,
555 		.ctrlbit	= S3C2412_CLKCON_UART1,
556 	}, {
557 		.name		= "uart",
558 		.id		= 2,
559 		.parent		= &clk_p,
560 		.enable		= s3c2412_clkcon_enable,
561 		.ctrlbit	= S3C2412_CLKCON_UART2,
562 	}, {
563 		.name		= "rtc",
564 		.id		= -1,
565 		.parent		= &clk_p,
566 		.enable		= s3c2412_clkcon_enable,
567 		.ctrlbit	= S3C2412_CLKCON_RTC,
568 	}, {
569 		.name		= "watchdog",
570 		.id		= -1,
571 		.parent		= &clk_p,
572 		.ctrlbit	= 0,
573 	}, {
574 		.name		= "usb-bus-gadget",
575 		.id		= -1,
576 		.parent		= &clk_usb_bus,
577 		.enable		= s3c2412_clkcon_enable,
578 		.ctrlbit	= S3C2412_CLKCON_USB_DEV48,
579 	}, {
580 		.name		= "usb-bus-host",
581 		.id		= -1,
582 		.parent		= &clk_usb_bus,
583 		.enable		= s3c2412_clkcon_enable,
584 		.ctrlbit	= S3C2412_CLKCON_USB_HOST48,
585 	}
586 };
587 
588 /* clocks to add where we need to check their parentage */
589 
590 struct clk_init {
591 	struct clk	*clk;
592 	unsigned int	 bit;
593 	struct clk	*src_0;
594 	struct clk	*src_1;
595 };
596 
597 static struct clk_init clks_src[] __initdata = {
598 	{
599 		.clk	= &clk_usysclk,
600 		.bit	= S3C2412_CLKSRC_USBCLK_HCLK,
601 		.src_0	= &clk_urefclk,
602 		.src_1	= &clk_upll,
603 	}, {
604 		.clk	= &clk_i2s,
605 		.bit	= S3C2412_CLKSRC_I2SCLK_MPLL,
606 		.src_0	= &clk_erefclk,
607 		.src_1	= &clk_mpll,
608 	}, {
609 		.clk	= &clk_cam,
610 		.bit	= S3C2412_CLKSRC_CAMCLK_HCLK,
611 		.src_0	= &clk_usysclk,
612 		.src_1	= &clk_h,
613 	}, {
614 		.clk	= &clk_msysclk,
615 		.bit	= S3C2412_CLKSRC_MSYSCLK_MPLL,
616 		.src_0	= &clk_mdivclk,
617 		.src_1	= &clk_mpll,
618 	}, {
619 		.clk	= &clk_uart,
620 		.bit	= S3C2412_CLKSRC_UARTCLK_MPLL,
621 		.src_0	= &clk_erefclk,
622 		.src_1	= &clk_mpll,
623 	}, {
624 		.clk	= &clk_usbsrc,
625 		.bit	= S3C2412_CLKSRC_USBCLK_HCLK,
626 		.src_0	= &clk_usysclk,
627 		.src_1	= &clk_h,
628 	/* here we assume  OM[4] select xtal */
629 	}, {
630 		.clk	= &clk_erefclk,
631 		.bit	= S3C2412_CLKSRC_EREFCLK_EXTCLK,
632 		.src_0	= &clk_xtal,
633 		.src_1	= &clk_ext,
634 	}, {
635 		.clk	= &clk_urefclk,
636 		.bit	= S3C2412_CLKSRC_UREFCLK_EXTCLK,
637 		.src_0	= &clk_xtal,
638 		.src_1	= &clk_ext,
639 	},
640 };
641 
642 /* s3c2412_clk_initparents
643  *
644  * Initialise the parents for the clocks that we get at start-time
645 */
646 
s3c2412_clk_initparents(void)647 static void __init s3c2412_clk_initparents(void)
648 {
649 	unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
650 	struct clk_init *cip = clks_src;
651 	struct clk *src;
652 	int ptr;
653 	int ret;
654 
655 	for (ptr = 0; ptr < ARRAY_SIZE(clks_src); ptr++, cip++) {
656 		ret = s3c24xx_register_clock(cip->clk);
657 		if (ret < 0) {
658 			printk(KERN_ERR "Failed to register clock %s (%d)\n",
659 			       cip->clk->name, ret);
660 		}
661 
662 		src = (clksrc & cip->bit) ? cip->src_1 : cip->src_0;
663 
664 		printk(KERN_INFO "%s: parent %s\n", cip->clk->name, src->name);
665 		clk_set_parent(cip->clk, src);
666 	}
667 }
668 
669 /* clocks to add straight away */
670 
671 static struct clk *clks[] __initdata = {
672 	&clk_ext,
673 	&clk_usb_bus,
674 	&clk_mrefclk,
675 	&clk_armclk,
676 };
677 
s3c2412_baseclk_add(void)678 int __init s3c2412_baseclk_add(void)
679 {
680 	unsigned long clkcon  = __raw_readl(S3C2410_CLKCON);
681 	unsigned int dvs;
682 	struct clk *clkp;
683 	int ret;
684 	int ptr;
685 
686 	clk_upll.enable = s3c2412_upll_enable;
687 	clk_usb_bus.parent = &clk_usbsrc;
688 	clk_usb_bus.rate = 0x0;
689 
690 	clk_f.parent = &clk_msysclk;
691 
692 	s3c2412_clk_initparents();
693 
694 	for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
695 		clkp = clks[ptr];
696 
697 		ret = s3c24xx_register_clock(clkp);
698 		if (ret < 0) {
699 			printk(KERN_ERR "Failed to register clock %s (%d)\n",
700 			       clkp->name, ret);
701 		}
702 	}
703 
704 	/* set the dvs state according to what we got at boot time */
705 
706 	dvs = __raw_readl(S3C2410_CLKDIVN) & S3C2412_CLKDIVN_DVSEN;
707 
708 	if (dvs)
709 		clk_armclk.parent = &clk_h;
710 
711 	printk(KERN_INFO "S3C2412: DVS is %s\n", dvs ? "on" : "off");
712 
713 	/* ensure usb bus clock is within correct rate of 48MHz */
714 
715 	if (clk_get_rate(&clk_usb_bus) != (48 * 1000 * 1000)) {
716 		printk(KERN_INFO "Warning: USB bus clock not at 48MHz\n");
717 
718 		/* for the moment, let's use the UPLL, and see if we can
719 		 * get 48MHz */
720 
721 		clk_set_parent(&clk_usysclk, &clk_upll);
722 		clk_set_parent(&clk_usbsrc, &clk_usysclk);
723 		clk_set_rate(&clk_usbsrc, 48*1000*1000);
724 	}
725 
726 	printk("S3C2412: upll %s, %ld.%03ld MHz, usb-bus %ld.%03ld MHz\n",
727 	       (__raw_readl(S3C2410_UPLLCON) & S3C2412_PLLCON_OFF) ? "off":"on",
728 	       print_mhz(clk_get_rate(&clk_upll)),
729 	       print_mhz(clk_get_rate(&clk_usb_bus)));
730 
731 	/* register clocks from clock array */
732 
733 	clkp = init_clocks;
734 	for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
735 		/* ensure that we note the clock state */
736 
737 		clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
738 
739 		ret = s3c24xx_register_clock(clkp);
740 		if (ret < 0) {
741 			printk(KERN_ERR "Failed to register clock %s (%d)\n",
742 			       clkp->name, ret);
743 		}
744 	}
745 
746 	/* We must be careful disabling the clocks we are not intending to
747 	 * be using at boot time, as subsystems such as the LCD which do
748 	 * their own DMA requests to the bus can cause the system to lockup
749 	 * if they where in the middle of requesting bus access.
750 	 *
751 	 * Disabling the LCD clock if the LCD is active is very dangerous,
752 	 * and therefore the bootloader should be careful to not enable
753 	 * the LCD clock if it is not needed.
754 	*/
755 
756 	/* install (and disable) the clocks we do not need immediately */
757 
758 	clkp = init_clocks_disable;
759 	for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
760 
761 		ret = s3c24xx_register_clock(clkp);
762 		if (ret < 0) {
763 			printk(KERN_ERR "Failed to register clock %s (%d)\n",
764 			       clkp->name, ret);
765 		}
766 
767 		s3c2412_clkcon_enable(clkp, 0);
768 	}
769 
770 	s3c_pwmclk_init();
771 	return 0;
772 }
773