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