1 /* linux/arch/arm/mach-s3c2410/clock.c
3 * Copyright (c) 2004-2005 Simtec Electronics
4 * Ben Dooks <ben@simtec.co.uk>
6 * S3C2410 Clock control support
8 * Based on, and code from linux/arch/arm/mach-versatile/clock.c
10 ** Copyright (C) 2004 ARM Limited.
11 ** Written by Deep Blue Solutions Limited.
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include <linux/init.h>
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/list.h>
33 #include <linux/errno.h>
34 #include <linux/err.h>
35 #include <linux/platform_device.h>
36 #include <linux/sysdev.h>
37 #include <linux/interrupt.h>
38 #include <linux/ioport.h>
39 #include <linux/clk.h>
40 #include <linux/mutex.h>
41 #include <linux/delay.h>
43 #include <asm/hardware.h>
47 #include <asm/arch/regs-clock.h>
48 #include <asm/arch/regs-gpio.h>
53 /* clock information */
55 static LIST_HEAD(clocks
);
57 DEFINE_MUTEX(clocks_mutex
);
61 void inline s3c24xx_clk_enable(unsigned int clocks
, unsigned int enable
)
65 clkcon
= __raw_readl(S3C2410_CLKCON
);
72 /* ensure none of the special function bits set */
73 clkcon
&= ~(S3C2410_CLKCON_IDLE
|S3C2410_CLKCON_POWER
);
75 __raw_writel(clkcon
, S3C2410_CLKCON
);
78 /* enable and disable calls for use with the clk struct */
80 static int clk_null_enable(struct clk
*clk
, int enable
)
85 int s3c24xx_clkcon_enable(struct clk
*clk
, int enable
)
87 s3c24xx_clk_enable(clk
->ctrlbit
, enable
);
93 struct clk
*clk_get(struct device
*dev
, const char *id
)
96 struct clk
*clk
= ERR_PTR(-ENOENT
);
99 if (dev
== NULL
|| dev
->bus
!= &platform_bus_type
)
102 idno
= to_platform_device(dev
)->id
;
104 mutex_lock(&clocks_mutex
);
106 list_for_each_entry(p
, &clocks
, list
) {
108 strcmp(id
, p
->name
) == 0 &&
109 try_module_get(p
->owner
)) {
115 /* check for the case where a device was supplied, but the
116 * clock that was being searched for is not device specific */
119 list_for_each_entry(p
, &clocks
, list
) {
120 if (p
->id
== -1 && strcmp(id
, p
->name
) == 0 &&
121 try_module_get(p
->owner
)) {
128 mutex_unlock(&clocks_mutex
);
132 void clk_put(struct clk
*clk
)
134 module_put(clk
->owner
);
137 int clk_enable(struct clk
*clk
)
139 if (IS_ERR(clk
) || clk
== NULL
)
142 clk_enable(clk
->parent
);
144 mutex_lock(&clocks_mutex
);
146 if ((clk
->usage
++) == 0)
147 (clk
->enable
)(clk
, 1);
149 mutex_unlock(&clocks_mutex
);
153 void clk_disable(struct clk
*clk
)
155 if (IS_ERR(clk
) || clk
== NULL
)
158 mutex_lock(&clocks_mutex
);
160 if ((--clk
->usage
) == 0)
161 (clk
->enable
)(clk
, 0);
163 mutex_unlock(&clocks_mutex
);
164 clk_disable(clk
->parent
);
168 unsigned long clk_get_rate(struct clk
*clk
)
176 while (clk
->parent
!= NULL
&& clk
->rate
== 0)
182 long clk_round_rate(struct clk
*clk
, unsigned long rate
)
184 if (!IS_ERR(clk
) && clk
->round_rate
)
185 return (clk
->round_rate
)(clk
, rate
);
190 int clk_set_rate(struct clk
*clk
, unsigned long rate
)
197 mutex_lock(&clocks_mutex
);
198 ret
= (clk
->set_rate
)(clk
, rate
);
199 mutex_unlock(&clocks_mutex
);
204 struct clk
*clk_get_parent(struct clk
*clk
)
209 int clk_set_parent(struct clk
*clk
, struct clk
*parent
)
216 mutex_lock(&clocks_mutex
);
219 ret
= (clk
->set_parent
)(clk
, parent
);
221 mutex_unlock(&clocks_mutex
);
226 EXPORT_SYMBOL(clk_get
);
227 EXPORT_SYMBOL(clk_put
);
228 EXPORT_SYMBOL(clk_enable
);
229 EXPORT_SYMBOL(clk_disable
);
230 EXPORT_SYMBOL(clk_get_rate
);
231 EXPORT_SYMBOL(clk_round_rate
);
232 EXPORT_SYMBOL(clk_set_rate
);
233 EXPORT_SYMBOL(clk_get_parent
);
234 EXPORT_SYMBOL(clk_set_parent
);
236 /* base clock enable */
238 static int s3c24xx_upll_enable(struct clk
*clk
, int enable
)
240 unsigned long clkslow
= __raw_readl(S3C2410_CLKSLOW
);
241 unsigned long orig
= clkslow
;
244 clkslow
&= ~S3C2410_CLKSLOW_UCLK_OFF
;
246 clkslow
|= S3C2410_CLKSLOW_UCLK_OFF
;
248 __raw_writel(clkslow
, S3C2410_CLKSLOW
);
250 /* if we started the UPLL, then allow to settle */
252 if (enable
&& !(orig
& S3C2410_CLKSLOW_UCLK_OFF
))
260 static struct clk clk_xtal
= {
268 static struct clk clk_upll
= {
272 .enable
= s3c24xx_upll_enable
,
276 static struct clk clk_f
= {
284 static struct clk clk_h
= {
292 static struct clk clk_p
= {
300 struct clk clk_usb_bus
= {
307 /* clocks that could be registered by external code */
309 static int s3c24xx_dclk_enable(struct clk
*clk
, int enable
)
311 unsigned long dclkcon
= __raw_readl(S3C2410_DCLKCON
);
314 dclkcon
|= clk
->ctrlbit
;
316 dclkcon
&= ~clk
->ctrlbit
;
318 __raw_writel(dclkcon
, S3C2410_DCLKCON
);
323 static int s3c24xx_dclk_setparent(struct clk
*clk
, struct clk
*parent
)
325 unsigned long dclkcon
;
328 if (parent
== &clk_upll
)
330 else if (parent
== &clk_p
)
335 clk
->parent
= parent
;
337 dclkcon
= __raw_readl(S3C2410_DCLKCON
);
339 if (clk
->ctrlbit
== S3C2410_DCLKCON_DCLK0EN
) {
341 dclkcon
|= S3C2410_DCLKCON_DCLK0_UCLK
;
343 dclkcon
&= ~S3C2410_DCLKCON_DCLK0_UCLK
;
346 dclkcon
|= S3C2410_DCLKCON_DCLK1_UCLK
;
348 dclkcon
&= ~S3C2410_DCLKCON_DCLK1_UCLK
;
351 __raw_writel(dclkcon
, S3C2410_DCLKCON
);
357 static int s3c24xx_clkout_setparent(struct clk
*clk
, struct clk
*parent
)
360 unsigned long source
;
362 /* calculate the MISCCR setting for the clock */
364 if (parent
== &clk_xtal
)
365 source
= S3C2410_MISCCR_CLK0_MPLL
;
366 else if (parent
== &clk_upll
)
367 source
= S3C2410_MISCCR_CLK0_UPLL
;
368 else if (parent
== &clk_f
)
369 source
= S3C2410_MISCCR_CLK0_FCLK
;
370 else if (parent
== &clk_p
)
371 source
= S3C2410_MISCCR_CLK0_PCLK
;
372 else if (clk
== &s3c24xx_clkout0
&& parent
== &s3c24xx_dclk0
)
373 source
= S3C2410_MISCCR_CLK0_DCLK0
;
374 else if (clk
== &s3c24xx_clkout1
&& parent
== &s3c24xx_dclk1
)
375 source
= S3C2410_MISCCR_CLK0_DCLK0
;
379 if (clk
== &s3c24xx_dclk0
)
380 mask
= S3C2410_MISCCR_CLK0_MASK
;
383 mask
= S3C2410_MISCCR_CLK1_MASK
;
386 s3c2410_modify_misccr(mask
, source
);
390 /* external clock definitions */
392 struct clk s3c24xx_dclk0
= {
395 .ctrlbit
= S3C2410_DCLKCON_DCLK0EN
,
396 .enable
= s3c24xx_dclk_enable
,
397 .set_parent
= s3c24xx_dclk_setparent
,
400 struct clk s3c24xx_dclk1
= {
403 .ctrlbit
= S3C2410_DCLKCON_DCLK0EN
,
404 .enable
= s3c24xx_dclk_enable
,
405 .set_parent
= s3c24xx_dclk_setparent
,
408 struct clk s3c24xx_clkout0
= {
411 .set_parent
= s3c24xx_clkout_setparent
,
414 struct clk s3c24xx_clkout1
= {
417 .set_parent
= s3c24xx_clkout_setparent
,
420 struct clk s3c24xx_uclk
= {
426 /* standard clock definitions */
428 static struct clk init_clocks
[] = {
433 .enable
= s3c24xx_clkcon_enable
,
434 .ctrlbit
= S3C2410_CLKCON_NAND
,
439 .enable
= s3c24xx_clkcon_enable
,
440 .ctrlbit
= S3C2410_CLKCON_LCDC
,
445 .enable
= s3c24xx_clkcon_enable
,
446 .ctrlbit
= S3C2410_CLKCON_USBH
,
448 .name
= "usb-device",
451 .enable
= s3c24xx_clkcon_enable
,
452 .ctrlbit
= S3C2410_CLKCON_USBD
,
457 .enable
= s3c24xx_clkcon_enable
,
458 .ctrlbit
= S3C2410_CLKCON_PWMT
,
463 .enable
= s3c24xx_clkcon_enable
,
464 .ctrlbit
= S3C2410_CLKCON_SDI
,
469 .enable
= s3c24xx_clkcon_enable
,
470 .ctrlbit
= S3C2410_CLKCON_UART0
,
475 .enable
= s3c24xx_clkcon_enable
,
476 .ctrlbit
= S3C2410_CLKCON_UART1
,
481 .enable
= s3c24xx_clkcon_enable
,
482 .ctrlbit
= S3C2410_CLKCON_UART2
,
487 .enable
= s3c24xx_clkcon_enable
,
488 .ctrlbit
= S3C2410_CLKCON_GPIO
,
493 .enable
= s3c24xx_clkcon_enable
,
494 .ctrlbit
= S3C2410_CLKCON_RTC
,
499 .enable
= s3c24xx_clkcon_enable
,
500 .ctrlbit
= S3C2410_CLKCON_ADC
,
505 .enable
= s3c24xx_clkcon_enable
,
506 .ctrlbit
= S3C2410_CLKCON_IIC
,
511 .enable
= s3c24xx_clkcon_enable
,
512 .ctrlbit
= S3C2410_CLKCON_IIS
,
517 .enable
= s3c24xx_clkcon_enable
,
518 .ctrlbit
= S3C2410_CLKCON_SPI
,
527 /* initialise the clock system */
529 int s3c24xx_register_clock(struct clk
*clk
)
531 clk
->owner
= THIS_MODULE
;
533 if (clk
->enable
== NULL
)
534 clk
->enable
= clk_null_enable
;
536 /* if this is a standard clock, set the usage state */
538 if (clk
->ctrlbit
&& clk
->enable
== s3c24xx_clkcon_enable
) {
539 unsigned long clkcon
= __raw_readl(S3C2410_CLKCON
);
541 clk
->usage
= (clkcon
& clk
->ctrlbit
) ? 1 : 0;
544 /* add to the list of available clocks */
546 mutex_lock(&clocks_mutex
);
547 list_add(&clk
->list
, &clocks
);
548 mutex_unlock(&clocks_mutex
);
553 /* initalise all the clocks */
555 int __init
s3c24xx_setup_clocks(unsigned long xtal
,
560 unsigned long upllcon
= __raw_readl(S3C2410_UPLLCON
);
561 unsigned long clkslow
= __raw_readl(S3C2410_CLKSLOW
);
562 struct clk
*clkp
= init_clocks
;
566 printk(KERN_INFO
"S3C2410 Clocks, (c) 2004 Simtec Electronics\n");
568 /* initialise the main system clocks */
570 clk_xtal
.rate
= xtal
;
571 clk_upll
.rate
= s3c2410_get_pll(upllcon
, xtal
);
577 /* We must be careful disabling the clocks we are not intending to
578 * be using at boot time, as subsytems such as the LCD which do
579 * their own DMA requests to the bus can cause the system to lockup
580 * if they where in the middle of requesting bus access.
582 * Disabling the LCD clock if the LCD is active is very dangerous,
583 * and therefore the bootloader should be careful to not enable
584 * the LCD clock if it is not needed.
587 mutex_lock(&clocks_mutex
);
589 s3c24xx_clk_enable(S3C2410_CLKCON_NAND
, 0);
590 s3c24xx_clk_enable(S3C2410_CLKCON_USBH
, 0);
591 s3c24xx_clk_enable(S3C2410_CLKCON_USBD
, 0);
592 s3c24xx_clk_enable(S3C2410_CLKCON_ADC
, 0);
593 s3c24xx_clk_enable(S3C2410_CLKCON_IIC
, 0);
594 s3c24xx_clk_enable(S3C2410_CLKCON_SPI
, 0);
596 mutex_unlock(&clocks_mutex
);
598 /* assume uart clocks are correctly setup */
600 /* register our clocks */
602 if (s3c24xx_register_clock(&clk_xtal
) < 0)
603 printk(KERN_ERR
"failed to register master xtal\n");
605 if (s3c24xx_register_clock(&clk_upll
) < 0)
606 printk(KERN_ERR
"failed to register upll clock\n");
608 if (s3c24xx_register_clock(&clk_f
) < 0)
609 printk(KERN_ERR
"failed to register cpu fclk\n");
611 if (s3c24xx_register_clock(&clk_h
) < 0)
612 printk(KERN_ERR
"failed to register cpu hclk\n");
614 if (s3c24xx_register_clock(&clk_p
) < 0)
615 printk(KERN_ERR
"failed to register cpu pclk\n");
618 if (s3c24xx_register_clock(&clk_usb_bus
) < 0)
619 printk(KERN_ERR
"failed to register usb bus clock\n");
621 /* register clocks from clock array */
623 for (ptr
= 0; ptr
< ARRAY_SIZE(init_clocks
); ptr
++, clkp
++) {
624 ret
= s3c24xx_register_clock(clkp
);
626 printk(KERN_ERR
"Failed to register clock %s (%d)\n",
631 /* show the clock-slow value */
633 printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
634 print_mhz(xtal
/ ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow
))),
635 (clkslow
& S3C2410_CLKSLOW_SLOW
) ? "slow" : "fast",
636 (clkslow
& S3C2410_CLKSLOW_MPLL_OFF
) ? "off" : "on",
637 (clkslow
& S3C2410_CLKSLOW_UCLK_OFF
) ? "off" : "on");