Commit | Line | Data |
---|---|---|
09ec1d7e | 1 | /* |
a21765a7 | 2 | * Copyright (c) 2006 Simtec Electronics |
1da177e4 LT |
3 | * Ben Dooks <ben@simtec.co.uk> |
4 | * | |
a21765a7 | 5 | * S3C2410,S3C2440,S3C2442 Clock control support |
1da177e4 LT |
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> | |
edbaa603 | 28 | #include <linux/device.h> |
f8ce2547 | 29 | #include <linux/clk.h> |
00431707 | 30 | #include <linux/mutex.h> |
8e40a2f9 | 31 | #include <linux/delay.h> |
a21765a7 | 32 | #include <linux/serial_core.h> |
fced80c7 | 33 | #include <linux/io.h> |
a21765a7 BD |
34 | |
35 | #include <asm/mach/map.h> | |
1da177e4 | 36 | |
a09e64fb | 37 | #include <mach/hardware.h> |
1da177e4 | 38 | |
a2b7ba9c | 39 | #include <plat/regs-serial.h> |
a09e64fb RK |
40 | #include <mach/regs-clock.h> |
41 | #include <mach/regs-gpio.h> | |
1da177e4 | 42 | |
d5120ae7 | 43 | #include <plat/clock.h> |
a2b7ba9c | 44 | #include <plat/cpu.h> |
1da177e4 | 45 | |
a21765a7 | 46 | int s3c2410_clkcon_enable(struct clk *clk, int enable) |
1da177e4 | 47 | { |
a21765a7 BD |
48 | unsigned int clocks = clk->ctrlbit; |
49 | unsigned long clkcon; | |
1da177e4 | 50 | |
a21765a7 | 51 | clkcon = __raw_readl(S3C2410_CLKCON); |
1da177e4 | 52 | |
a21765a7 BD |
53 | if (enable) |
54 | clkcon |= clocks; | |
c086f282 | 55 | else |
a21765a7 | 56 | clkcon &= ~clocks; |
1da177e4 | 57 | |
a21765a7 BD |
58 | /* ensure none of the special function bits set */ |
59 | clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); | |
1da177e4 | 60 | |
a21765a7 | 61 | __raw_writel(clkcon, S3C2410_CLKCON); |
1da177e4 | 62 | |
2a513ce7 | 63 | return 0; |
1da177e4 LT |
64 | } |
65 | ||
a21765a7 | 66 | static int s3c2410_upll_enable(struct clk *clk, int enable) |
1da177e4 | 67 | { |
a21765a7 BD |
68 | unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); |
69 | unsigned long orig = clkslow; | |
3fc3e1c0 BD |
70 | |
71 | if (enable) | |
a21765a7 | 72 | clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; |
3fc3e1c0 | 73 | else |
a21765a7 | 74 | clkslow |= S3C2410_CLKSLOW_UCLK_OFF; |
3fc3e1c0 | 75 | |
a21765a7 | 76 | __raw_writel(clkslow, S3C2410_CLKSLOW); |
3fc3e1c0 | 77 | |
a21765a7 | 78 | /* if we started the UPLL, then allow to settle */ |
3fc3e1c0 | 79 | |
a21765a7 BD |
80 | if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) |
81 | udelay(200); | |
3fc3e1c0 BD |
82 | |
83 | return 0; | |
84 | } | |
85 | ||
a21765a7 BD |
86 | /* standard clock definitions */ |
87 | ||
4e04691b | 88 | static struct clk init_clocks_off[] = { |
a21765a7 BD |
89 | { |
90 | .name = "nand", | |
a21765a7 BD |
91 | .parent = &clk_h, |
92 | .enable = s3c2410_clkcon_enable, | |
93 | .ctrlbit = S3C2410_CLKCON_NAND, | |
94 | }, { | |
95 | .name = "sdi", | |
a21765a7 BD |
96 | .parent = &clk_p, |
97 | .enable = s3c2410_clkcon_enable, | |
98 | .ctrlbit = S3C2410_CLKCON_SDI, | |
99 | }, { | |
100 | .name = "adc", | |
a21765a7 BD |
101 | .parent = &clk_p, |
102 | .enable = s3c2410_clkcon_enable, | |
103 | .ctrlbit = S3C2410_CLKCON_ADC, | |
104 | }, { | |
105 | .name = "i2c", | |
a21765a7 BD |
106 | .parent = &clk_p, |
107 | .enable = s3c2410_clkcon_enable, | |
108 | .ctrlbit = S3C2410_CLKCON_IIC, | |
109 | }, { | |
110 | .name = "iis", | |
a21765a7 BD |
111 | .parent = &clk_p, |
112 | .enable = s3c2410_clkcon_enable, | |
113 | .ctrlbit = S3C2410_CLKCON_IIS, | |
114 | }, { | |
115 | .name = "spi", | |
a21765a7 BD |
116 | .parent = &clk_p, |
117 | .enable = s3c2410_clkcon_enable, | |
118 | .ctrlbit = S3C2410_CLKCON_SPI, | |
3fc3e1c0 | 119 | } |
1da177e4 LT |
120 | }; |
121 | ||
a21765a7 BD |
122 | static struct clk init_clocks[] = { |
123 | { | |
124 | .name = "lcd", | |
a21765a7 BD |
125 | .parent = &clk_h, |
126 | .enable = s3c2410_clkcon_enable, | |
127 | .ctrlbit = S3C2410_CLKCON_LCDC, | |
128 | }, { | |
129 | .name = "gpio", | |
a21765a7 BD |
130 | .parent = &clk_p, |
131 | .enable = s3c2410_clkcon_enable, | |
132 | .ctrlbit = S3C2410_CLKCON_GPIO, | |
133 | }, { | |
134 | .name = "usb-host", | |
a21765a7 BD |
135 | .parent = &clk_h, |
136 | .enable = s3c2410_clkcon_enable, | |
137 | .ctrlbit = S3C2410_CLKCON_USBH, | |
138 | }, { | |
139 | .name = "usb-device", | |
a21765a7 BD |
140 | .parent = &clk_h, |
141 | .enable = s3c2410_clkcon_enable, | |
142 | .ctrlbit = S3C2410_CLKCON_USBD, | |
143 | }, { | |
144 | .name = "timers", | |
a21765a7 BD |
145 | .parent = &clk_p, |
146 | .enable = s3c2410_clkcon_enable, | |
147 | .ctrlbit = S3C2410_CLKCON_PWMT, | |
148 | }, { | |
149 | .name = "uart", | |
e83626f2 | 150 | .devname = "s3c2410-uart.0", |
a21765a7 BD |
151 | .parent = &clk_p, |
152 | .enable = s3c2410_clkcon_enable, | |
153 | .ctrlbit = S3C2410_CLKCON_UART0, | |
154 | }, { | |
155 | .name = "uart", | |
e83626f2 | 156 | .devname = "s3c2410-uart.1", |
a21765a7 BD |
157 | .parent = &clk_p, |
158 | .enable = s3c2410_clkcon_enable, | |
159 | .ctrlbit = S3C2410_CLKCON_UART1, | |
160 | }, { | |
161 | .name = "uart", | |
e83626f2 | 162 | .devname = "s3c2410-uart.2", |
a21765a7 BD |
163 | .parent = &clk_p, |
164 | .enable = s3c2410_clkcon_enable, | |
165 | .ctrlbit = S3C2410_CLKCON_UART2, | |
166 | }, { | |
167 | .name = "rtc", | |
a21765a7 BD |
168 | .parent = &clk_p, |
169 | .enable = s3c2410_clkcon_enable, | |
170 | .ctrlbit = S3C2410_CLKCON_RTC, | |
171 | }, { | |
172 | .name = "watchdog", | |
a21765a7 BD |
173 | .parent = &clk_p, |
174 | .ctrlbit = 0, | |
175 | }, { | |
176 | .name = "usb-bus-host", | |
a21765a7 BD |
177 | .parent = &clk_usb_bus, |
178 | }, { | |
179 | .name = "usb-bus-gadget", | |
a21765a7 BD |
180 | .parent = &clk_usb_bus, |
181 | }, | |
1da177e4 LT |
182 | }; |
183 | ||
a21765a7 BD |
184 | /* s3c2410_baseclk_add() |
185 | * | |
186 | * Add all the clocks used by the s3c2410 or compatible CPUs | |
187 | * such as the S3C2440 and S3C2442. | |
188 | * | |
189 | * We cannot use a system device as we are needed before any | |
190 | * of the init-calls that initialise the devices are actually | |
191 | * done. | |
192 | */ | |
1da177e4 | 193 | |
a21765a7 | 194 | int __init s3c2410_baseclk_add(void) |
1da177e4 | 195 | { |
a21765a7 BD |
196 | unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); |
197 | unsigned long clkcon = __raw_readl(S3C2410_CLKCON); | |
198 | struct clk *clkp; | |
199 | struct clk *xtal; | |
200 | int ret; | |
201 | int ptr; | |
1da177e4 | 202 | |
a21765a7 | 203 | clk_upll.enable = s3c2410_upll_enable; |
1da177e4 | 204 | |
a21765a7 BD |
205 | if (s3c24xx_register_clock(&clk_usb_bus) < 0) |
206 | printk(KERN_ERR "failed to register usb bus clock\n"); | |
1da177e4 | 207 | |
a21765a7 | 208 | /* register clocks from clock array */ |
1da177e4 | 209 | |
a21765a7 BD |
210 | clkp = init_clocks; |
211 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { | |
212 | /* ensure that we note the clock state */ | |
1da177e4 | 213 | |
a21765a7 | 214 | clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0; |
1da177e4 | 215 | |
a21765a7 BD |
216 | ret = s3c24xx_register_clock(clkp); |
217 | if (ret < 0) { | |
218 | printk(KERN_ERR "Failed to register clock %s (%d)\n", | |
219 | clkp->name, ret); | |
220 | } | |
221 | } | |
1da177e4 | 222 | |
a21765a7 | 223 | /* We must be careful disabling the clocks we are not intending to |
3a4fa0a2 | 224 | * be using at boot time, as subsystems such as the LCD which do |
a21765a7 BD |
225 | * their own DMA requests to the bus can cause the system to lockup |
226 | * if they where in the middle of requesting bus access. | |
227 | * | |
228 | * Disabling the LCD clock if the LCD is active is very dangerous, | |
229 | * and therefore the bootloader should be careful to not enable | |
230 | * the LCD clock if it is not needed. | |
231 | */ | |
232 | ||
233 | /* install (and disable) the clocks we do not need immediately */ | |
234 | ||
4e04691b BD |
235 | s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); |
236 | s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); | |
8e40a2f9 | 237 | |
a21765a7 | 238 | /* show the clock-slow value */ |
1da177e4 | 239 | |
a21765a7 | 240 | xtal = clk_get(NULL, "xtal"); |
1da177e4 | 241 | |
a21765a7 BD |
242 | printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n", |
243 | print_mhz(clk_get_rate(xtal) / | |
244 | ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))), | |
245 | (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast", | |
246 | (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", | |
247 | (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); | |
1da177e4 | 248 | |
9d325f23 | 249 | s3c_pwmclk_init(); |
1da177e4 LT |
250 | return 0; |
251 | } |