aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s3c2410/s3c2410-clock.c
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2006-06-22 17:18:20 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-06-22 17:18:20 -0400
commit99c13853ffa26dd6527995b3f47548e075f201fb (patch)
treeb007bb3e79739f7f442daa00f92a34b1348cd29b /arch/arm/mach-s3c2410/s3c2410-clock.c
parenta341305e94982c66a2e94125a24b860605da9066 (diff)
[ARM] 3627/1: S3C24XX: split s3c2410 clocks from core clocks
Patch from Ben Dooks Split the s3c2410 specific clocks from the core clock code, as part of the work to support more of the Samsung line of SoCs. The patch does not use the sysdev mechanism as the clocks are needed for the timer init, which is very early in the kernel init sequence. Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-s3c2410/s3c2410-clock.c')
-rw-r--r--arch/arm/mach-s3c2410/s3c2410-clock.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c2410/s3c2410-clock.c b/arch/arm/mach-s3c2410/s3c2410-clock.c
new file mode 100644
index 000000000000..fd17c60e1132
--- /dev/null
+++ b/arch/arm/mach-s3c2410/s3c2410-clock.c
@@ -0,0 +1,263 @@
1/* linux/arch/arm/mach-s3c2410/clock.c
2 *
3 * Copyright (c) 2006 Simtec Electronics
4 * Ben Dooks <ben@simtec.co.uk>
5 *
6 * S3C2410,S3C2440,S3C2442 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
34#include <asm/hardware.h>
35#include <asm/io.h>
36
37#include <asm/arch/regs-clock.h>
38#include <asm/arch/regs-gpio.h>
39
40#include "clock.h"
41#include "cpu.h"
42
43int s3c2410_clkcon_enable(struct clk *clk, int enable)
44{
45 unsigned int clocks = clk->ctrlbit;
46 unsigned long clkcon;
47
48 clkcon = __raw_readl(S3C2410_CLKCON);
49
50 if (enable)
51 clkcon |= clocks;
52 else
53 clkcon &= ~clocks;
54
55 /* ensure none of the special function bits set */
56 clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
57
58 __raw_writel(clkcon, S3C2410_CLKCON);
59
60 return 0;
61}
62
63static int s3c2410_upll_enable(struct clk *clk, int enable)
64{
65 unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
66 unsigned long orig = clkslow;
67
68 if (enable)
69 clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
70 else
71 clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
72
73 __raw_writel(clkslow, S3C2410_CLKSLOW);
74
75 /* if we started the UPLL, then allow to settle */
76
77 if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF))
78 udelay(200);
79
80 return 0;
81}
82
83/* standard clock definitions */
84
85static struct clk init_clocks_disable[] = {
86 {
87 .name = "nand",
88 .id = -1,
89 .parent = &clk_h,
90 .enable = s3c2410_clkcon_enable,
91 .ctrlbit = S3C2410_CLKCON_NAND,
92 }, {
93 .name = "sdi",
94 .id = -1,
95 .parent = &clk_p,
96 .enable = s3c2410_clkcon_enable,
97 .ctrlbit = S3C2410_CLKCON_SDI,
98 }, {
99 .name = "adc",
100 .id = -1,
101 .parent = &clk_p,
102 .enable = s3c2410_clkcon_enable,
103 .ctrlbit = S3C2410_CLKCON_ADC,
104 }, {
105 .name = "i2c",
106 .id = -1,
107 .parent = &clk_p,
108 .enable = s3c2410_clkcon_enable,
109 .ctrlbit = S3C2410_CLKCON_IIC,
110 }, {
111 .name = "iis",
112 .id = -1,
113 .parent = &clk_p,
114 .enable = s3c2410_clkcon_enable,
115 .ctrlbit = S3C2410_CLKCON_IIS,
116 }, {
117 .name = "spi",
118 .id = -1,
119 .parent = &clk_p,
120 .enable = s3c2410_clkcon_enable,
121 .ctrlbit = S3C2410_CLKCON_SPI,
122 }
123};
124
125static struct clk init_clocks[] = {
126 {
127 .name = "lcd",
128 .id = -1,
129 .parent = &clk_h,
130 .enable = s3c2410_clkcon_enable,
131 .ctrlbit = S3C2410_CLKCON_LCDC,
132 }, {
133 .name = "gpio",
134 .id = -1,
135 .parent = &clk_p,
136 .enable = s3c2410_clkcon_enable,
137 .ctrlbit = S3C2410_CLKCON_GPIO,
138 }, {
139 .name = "usb-host",
140 .id = -1,
141 .parent = &clk_h,
142 .enable = s3c2410_clkcon_enable,
143 .ctrlbit = S3C2410_CLKCON_USBH,
144 }, {
145 .name = "usb-device",
146 .id = -1,
147 .parent = &clk_h,
148 .enable = s3c2410_clkcon_enable,
149 .ctrlbit = S3C2410_CLKCON_USBD,
150 }, {
151 .name = "timers",
152 .id = -1,
153 .parent = &clk_p,
154 .enable = s3c2410_clkcon_enable,
155 .ctrlbit = S3C2410_CLKCON_PWMT,
156 }, {
157 .name = "uart",
158 .id = 0,
159 .parent = &clk_p,
160 .enable = s3c2410_clkcon_enable,
161 .ctrlbit = S3C2410_CLKCON_UART0,
162 }, {
163 .name = "uart",
164 .id = 1,
165 .parent = &clk_p,
166 .enable = s3c2410_clkcon_enable,
167 .ctrlbit = S3C2410_CLKCON_UART1,
168 }, {
169 .name = "uart",
170 .id = 2,
171 .parent = &clk_p,
172 .enable = s3c2410_clkcon_enable,
173 .ctrlbit = S3C2410_CLKCON_UART2,
174 }, {
175 .name = "rtc",
176 .id = -1,
177 .parent = &clk_p,
178 .enable = s3c2410_clkcon_enable,
179 .ctrlbit = S3C2410_CLKCON_RTC,
180 }, {
181 .name = "watchdog",
182 .id = -1,
183 .parent = &clk_p,
184 .ctrlbit = 0,
185 }
186};
187
188/* s3c2410_baseclk_add()
189 *
190 * Add all the clocks used by the s3c2410 or compatible CPUs
191 * such as the S3C2440 and S3C2442.
192 *
193 * We cannot use a system device as we are needed before any
194 * of the init-calls that initialise the devices are actually
195 * done.
196*/
197
198int __init s3c2410_baseclk_add(void)
199{
200 unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
201 unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
202 struct clk *clkp;
203 struct clk *xtal;
204 int ret;
205 int ptr;
206
207 clk_upll.enable = s3c2410_upll_enable;
208
209 if (s3c24xx_register_clock(&clk_usb_bus) < 0)
210 printk(KERN_ERR "failed to register usb bus clock\n");
211
212 /* register clocks from clock array */
213
214 clkp = init_clocks;
215 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
216 /* ensure that we note the clock state */
217
218 clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
219
220 ret = s3c24xx_register_clock(clkp);
221 if (ret < 0) {
222 printk(KERN_ERR "Failed to register clock %s (%d)\n",
223 clkp->name, ret);
224 }
225 }
226
227 /* We must be careful disabling the clocks we are not intending to
228 * be using at boot time, as subsytems such as the LCD which do
229 * their own DMA requests to the bus can cause the system to lockup
230 * if they where in the middle of requesting bus access.
231 *
232 * Disabling the LCD clock if the LCD is active is very dangerous,
233 * and therefore the bootloader should be careful to not enable
234 * the LCD clock if it is not needed.
235 */
236
237 /* install (and disable) the clocks we do not need immediately */
238
239 clkp = init_clocks_disable;
240 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
241
242 ret = s3c24xx_register_clock(clkp);
243 if (ret < 0) {
244 printk(KERN_ERR "Failed to register clock %s (%d)\n",
245 clkp->name, ret);
246 }
247
248 s3c2410_clkcon_enable(clkp, 0);
249 }
250
251 /* show the clock-slow value */
252
253 xtal = clk_get(NULL, "xtal");
254
255 printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
256 print_mhz(clk_get_rate(xtal) /
257 ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
258 (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
259 (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
260 (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
261
262 return 0;
263}