aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s3c24xx/clock-s3c2410.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-s3c24xx/clock-s3c2410.c')
-rw-r--r--arch/arm/mach-s3c24xx/clock-s3c2410.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c24xx/clock-s3c2410.c b/arch/arm/mach-s3c24xx/clock-s3c2410.c
new file mode 100644
index 000000000000..641266f3d152
--- /dev/null
+++ b/arch/arm/mach-s3c24xx/clock-s3c2410.c
@@ -0,0 +1,252 @@
1/*
2 * Copyright (c) 2006 Simtec Electronics
3 * Ben Dooks <ben@simtec.co.uk>
4 *
5 * S3C2410,S3C2440,S3C2442 Clock control support
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20*/
21
22#include <linux/init.h>
23#include <linux/module.h>
24#include <linux/kernel.h>
25#include <linux/list.h>
26#include <linux/errno.h>
27#include <linux/err.h>
28#include <linux/device.h>
29#include <linux/clk.h>
30#include <linux/mutex.h>
31#include <linux/delay.h>
32#include <linux/serial_core.h>
33#include <linux/io.h>
34
35#include <asm/mach/map.h>
36
37#include <mach/hardware.h>
38
39#include <plat/regs-serial.h>
40#include <mach/regs-clock.h>
41#include <mach/regs-gpio.h>
42
43#include <plat/s3c2410.h>
44#include <plat/clock.h>
45#include <plat/cpu.h>
46
47int s3c2410_clkcon_enable(struct clk *clk, int enable)
48{
49 unsigned int clocks = clk->ctrlbit;
50 unsigned long clkcon;
51
52 clkcon = __raw_readl(S3C2410_CLKCON);
53
54 if (enable)
55 clkcon |= clocks;
56 else
57 clkcon &= ~clocks;
58
59 /* ensure none of the special function bits set */
60 clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
61
62 __raw_writel(clkcon, S3C2410_CLKCON);
63
64 return 0;
65}
66
67static int s3c2410_upll_enable(struct clk *clk, int enable)
68{
69 unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
70 unsigned long orig = clkslow;
71
72 if (enable)
73 clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
74 else
75 clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
76
77 __raw_writel(clkslow, S3C2410_CLKSLOW);
78
79 /* if we started the UPLL, then allow to settle */
80
81 if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF))
82 udelay(200);
83
84 return 0;
85}
86
87/* standard clock definitions */
88
89static struct clk init_clocks_off[] = {
90 {
91 .name = "nand",
92 .parent = &clk_h,
93 .enable = s3c2410_clkcon_enable,
94 .ctrlbit = S3C2410_CLKCON_NAND,
95 }, {
96 .name = "sdi",
97 .parent = &clk_p,
98 .enable = s3c2410_clkcon_enable,
99 .ctrlbit = S3C2410_CLKCON_SDI,
100 }, {
101 .name = "adc",
102 .parent = &clk_p,
103 .enable = s3c2410_clkcon_enable,
104 .ctrlbit = S3C2410_CLKCON_ADC,
105 }, {
106 .name = "i2c",
107 .parent = &clk_p,
108 .enable = s3c2410_clkcon_enable,
109 .ctrlbit = S3C2410_CLKCON_IIC,
110 }, {
111 .name = "iis",
112 .parent = &clk_p,
113 .enable = s3c2410_clkcon_enable,
114 .ctrlbit = S3C2410_CLKCON_IIS,
115 }, {
116 .name = "spi",
117 .parent = &clk_p,
118 .enable = s3c2410_clkcon_enable,
119 .ctrlbit = S3C2410_CLKCON_SPI,
120 }
121};
122
123static struct clk init_clocks[] = {
124 {
125 .name = "lcd",
126 .parent = &clk_h,
127 .enable = s3c2410_clkcon_enable,
128 .ctrlbit = S3C2410_CLKCON_LCDC,
129 }, {
130 .name = "gpio",
131 .parent = &clk_p,
132 .enable = s3c2410_clkcon_enable,
133 .ctrlbit = S3C2410_CLKCON_GPIO,
134 }, {
135 .name = "usb-host",
136 .parent = &clk_h,
137 .enable = s3c2410_clkcon_enable,
138 .ctrlbit = S3C2410_CLKCON_USBH,
139 }, {
140 .name = "usb-device",
141 .parent = &clk_h,
142 .enable = s3c2410_clkcon_enable,
143 .ctrlbit = S3C2410_CLKCON_USBD,
144 }, {
145 .name = "timers",
146 .parent = &clk_p,
147 .enable = s3c2410_clkcon_enable,
148 .ctrlbit = S3C2410_CLKCON_PWMT,
149 }, {
150 .name = "uart",
151 .devname = "s3c2410-uart.0",
152 .parent = &clk_p,
153 .enable = s3c2410_clkcon_enable,
154 .ctrlbit = S3C2410_CLKCON_UART0,
155 }, {
156 .name = "uart",
157 .devname = "s3c2410-uart.1",
158 .parent = &clk_p,
159 .enable = s3c2410_clkcon_enable,
160 .ctrlbit = S3C2410_CLKCON_UART1,
161 }, {
162 .name = "uart",
163 .devname = "s3c2410-uart.2",
164 .parent = &clk_p,
165 .enable = s3c2410_clkcon_enable,
166 .ctrlbit = S3C2410_CLKCON_UART2,
167 }, {
168 .name = "rtc",
169 .parent = &clk_p,
170 .enable = s3c2410_clkcon_enable,
171 .ctrlbit = S3C2410_CLKCON_RTC,
172 }, {
173 .name = "watchdog",
174 .parent = &clk_p,
175 .ctrlbit = 0,
176 }, {
177 .name = "usb-bus-host",
178 .parent = &clk_usb_bus,
179 }, {
180 .name = "usb-bus-gadget",
181 .parent = &clk_usb_bus,
182 },
183};
184
185/* s3c2410_baseclk_add()
186 *
187 * Add all the clocks used by the s3c2410 or compatible CPUs
188 * such as the S3C2440 and S3C2442.
189 *
190 * We cannot use a system device as we are needed before any
191 * of the init-calls that initialise the devices are actually
192 * done.
193*/
194
195int __init s3c2410_baseclk_add(void)
196{
197 unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
198 unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
199 struct clk *clkp;
200 struct clk *xtal;
201 int ret;
202 int ptr;
203
204 clk_upll.enable = s3c2410_upll_enable;
205
206 if (s3c24xx_register_clock(&clk_usb_bus) < 0)
207 printk(KERN_ERR "failed to register usb bus clock\n");
208
209 /* register clocks from clock array */
210
211 clkp = init_clocks;
212 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
213 /* ensure that we note the clock state */
214
215 clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
216
217 ret = s3c24xx_register_clock(clkp);
218 if (ret < 0) {
219 printk(KERN_ERR "Failed to register clock %s (%d)\n",
220 clkp->name, ret);
221 }
222 }
223
224 /* We must be careful disabling the clocks we are not intending to
225 * be using at boot time, as subsystems such as the LCD which do
226 * their own DMA requests to the bus can cause the system to lockup
227 * if they where in the middle of requesting bus access.
228 *
229 * Disabling the LCD clock if the LCD is active is very dangerous,
230 * and therefore the bootloader should be careful to not enable
231 * the LCD clock if it is not needed.
232 */
233
234 /* install (and disable) the clocks we do not need immediately */
235
236 s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
237 s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
238
239 /* show the clock-slow value */
240
241 xtal = clk_get(NULL, "xtal");
242
243 printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
244 print_mhz(clk_get_rate(xtal) /
245 ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
246 (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
247 (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
248 (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
249
250 s3c_pwmclk_init();
251 return 0;
252}