Root/arch/arm/mach-s3c2412/clock.c

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

Archive Download this file



interactive